|
@@ -0,0 +1,2073 @@
|
|
|
+//
|
|
|
+// 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 BFCommonKit
|
|
|
+import Foundation
|
|
|
+import ObjectMapper
|
|
|
+import Photos
|
|
|
+import RealmSwift
|
|
|
+import UIKit
|
|
|
+
|
|
|
+class PQStuckPointEditerController: BFBaseViewController {
|
|
|
+ // 是否导出视频成功
|
|
|
+ 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: [PHAsset]? {
|
|
|
+ didSet {
|
|
|
+ if selectedPhotoData != nil, (selectedPhotoData?.count ?? 0) > 0 {
|
|
|
+ selectedMetarialData = Array<PQEditVisionTrackMaterialsModel>.init()
|
|
|
+ selectedPhotoData?.forEach { phAsset in
|
|
|
+ let metarialData = PQEditVisionTrackMaterialsModel()
|
|
|
+ metarialData.asset = phAsset
|
|
|
+ metarialData.width = Float(phAsset.pixelWidth)
|
|
|
+ metarialData.itemWidth = Float(phAsset.pixelWidth)
|
|
|
+ metarialData.height = Float(phAsset.pixelHeight)
|
|
|
+ metarialData.itemHeight = Float(phAsset.pixelHeight)
|
|
|
+ if phAsset.mediaType == .image {
|
|
|
+ metarialData.type = "image"
|
|
|
+ } else if phAsset.mediaType == .video {
|
|
|
+ metarialData.type = "video"
|
|
|
+ metarialData.duration = Float64(phAsset.duration)
|
|
|
+ }
|
|
|
+ metarialData.canvasFillType = phAsset.canvasFillType ?? ""
|
|
|
+ metarialData.locationPath = phAsset.localPath ?? ""
|
|
|
+ metarialData.selectedIndex = phAsset.selectedIndex ?? 1
|
|
|
+ metarialData.originalData = phAsset.originalData
|
|
|
+ metarialData.coverImageUI = phAsset.image
|
|
|
+ selectedMetarialData?.append(metarialData)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var selectedMetarialData: [PQEditVisionTrackMaterialsModel]?
|
|
|
+ // 选中的音乐数据
|
|
|
+ var stuckPointMusicData: PQVoiceModel?
|
|
|
+ // 保存所有段的所有贴纸,音频信息,用于播放器的渲染使用
|
|
|
+ var projectModel: PQEditProjectModel = PQEditProjectModel()
|
|
|
+ // 从草稿箱进入的项目数据
|
|
|
+ var draftProjectModel: PQEditProjectModel?
|
|
|
+ var mStickers: [PQEditVisionTrackMaterialsModel]?
|
|
|
+ // 播放器的开始和结束时间,1,刚进界面使用推荐的开始结束时间,2,用户修改起结点时修改
|
|
|
+ var playeTimeRange: CMTimeRange = CMTimeRange()
|
|
|
+
|
|
|
+ // 首帧图片
|
|
|
+ var firstFrameImage: UIImage? {
|
|
|
+ didSet {
|
|
|
+ if firstFrameImage != nil {
|
|
|
+ playerView.layer.contents = firstFrameImage?.cgImage
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // add by ak 是否是再创作模式
|
|
|
+ var isReCreate: Bool = false
|
|
|
+ public var reCreateVideoData: PQReCreateModel? // 再创作数据
|
|
|
+ // 最后一个选择的模式 BTN 用于还原选中状态
|
|
|
+ var lastEditModelBtn: UIButton?
|
|
|
+
|
|
|
+ // add by ak 最大、最小速度 有固定值和自定义,当快慢速下两个值都有效,当跳跃卡点只有maxSpeed有效
|
|
|
+ // 快慢速模式的 速度设置,快/慢速
|
|
|
+ var modelSpeed_maxSpeed: Float = 1.0
|
|
|
+ var modelSpeed_minSpeed: Float = 1.0
|
|
|
+ // 跳跃模式的速度
|
|
|
+ var modelPoint_speed: Float = 1.0
|
|
|
+
|
|
|
+ // 快慢速最后一次选择的速度位置
|
|
|
+ var lastSpeedSelectIndex: Int = 0
|
|
|
+ // 跳跃卡点最后一次选择的速度位置
|
|
|
+ var lastJumpSpeedSelectIndex: Int = 0
|
|
|
+ // 循环次数设置最后一次选择的位置
|
|
|
+ var lastCyclesSelectIndex: Int = -1
|
|
|
+
|
|
|
+ // 当前选择的玩法模式
|
|
|
+ var currentCreateStickersModel: createStickersModel = .createStickersModelSpeed
|
|
|
+
|
|
|
+ // 最终使用的卡点数据
|
|
|
+ var finallyStuckPoints: Array = Array<Float>.init()
|
|
|
+ var finallyStuckPointsInt64: Array = Array<Int64>.init()
|
|
|
+ // 最终使用的音频时长
|
|
|
+ var finallyUserAudioTime: Float = 0.0
|
|
|
+
|
|
|
+ // 注意推荐时间位置和后面最近的卡点时间与0.3的关系
|
|
|
+ // 保存丢卡点处理后的卡点信息推荐开始到最后倒数第二个
|
|
|
+ // 经过档位处理后的卡点信息
|
|
|
+ var stuckPointsTemp: Array = Array<Float>.init()
|
|
|
+ var stuckPointsTempInt64: Array = Array<Int64>.init()
|
|
|
+
|
|
|
+ // 是否点击了下一步去合成
|
|
|
+ var isClickNextBtn: Bool = false
|
|
|
+
|
|
|
+ // 下一步
|
|
|
+ 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: BFConfig.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 playerView = PQGPUImagePlayerView(frame: .zero)
|
|
|
+ playerView.backgroundColor = BFConfig.shared.styleBackGroundColor
|
|
|
+ playerView.isShowLine = false
|
|
|
+ playerView.showGaussianBlur = true
|
|
|
+ playerView.pause()
|
|
|
+ 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
|
|
|
+ // 改变速率,.只有快慢速且非只有图片素材时自动+1处理
|
|
|
+ self?.stuckPointMusicData?.speed = sender.tag
|
|
|
+ self?.musicEditBGView.pausePlayer()
|
|
|
+
|
|
|
+ self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.bgmInfo?.rhythmMusicSpeed = sender.tag
|
|
|
+ // 播放前先暂停
|
|
|
+// self?.playerView.stop()
|
|
|
+ // 开始播放
|
|
|
+ self?.settingPlayerView()
|
|
|
+
|
|
|
+ // 下面都是统计
|
|
|
+ if self?.currentCreateStickersModel == .createStickersModelPoint {
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectMusicVideoRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "点击上报:选择节奏")
|
|
|
+ } else if self?.currentCreateStickersModel == .createStickersModelSpeed {
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectSpeedRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "点击上报:选择节奏")
|
|
|
+ } else if self?.currentCreateStickersModel == .createStickersModelOnlyMusic {
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectMusicVideoRepeatRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, 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), suggestRhythmStartTime: CGFloat(stuckPointMusicData?.suggestRhythmStartTime ?? 0))
|
|
|
+ /// 裁剪进度回调
|
|
|
+ stuckPointCuttingView.videoDidBeginDrag = { [weak self] in
|
|
|
+ BFLog(message: "开始划动")
|
|
|
+ self?.playerView.pause()
|
|
|
+ }
|
|
|
+ /// 播放进度回调
|
|
|
+ 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(1, message: "拖拽结束返回--type = \(type),startTime = \(startTime),endTime = \(endTime),progress = \(progress)")
|
|
|
+ self?.playerView.pause()
|
|
|
+ self?.musicEditBGView.pausePlayer()
|
|
|
+
|
|
|
+ // 修改最新值
|
|
|
+ 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))
|
|
|
+
|
|
|
+ self?.dealParameter(model: self?.currentCreateStickersModel ?? .createStickersModelSpeed)
|
|
|
+ if (self?.finallyStuckPoints.count ?? 0) < 1 {
|
|
|
+ BFLog(message: "finallyStuckPoints data is error!!!!")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ DispatchQueue.global().async { // 并行、异步
|
|
|
+ let beginTime = Date()
|
|
|
+
|
|
|
+ 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)), model: self?.currentCreateStickersModel ?? .createStickersModelSpeed)
|
|
|
+
|
|
|
+ DispatchQueue.main.async { // 串行、异步
|
|
|
+ self?.playerView.mStickers = self?.mStickers
|
|
|
+ BFLog(message: "endTime is endTimeendTime \(Date().timeIntervalSince(beginTime))")
|
|
|
+ self?.playerView.play(pauseFirstFrame: false, playeTimeRange: self!.playeTimeRange)
|
|
|
+
|
|
|
+ // 更新一下时间条的UI总时间 及数据
|
|
|
+ self?.stuckPointCuttingView.videoDuration = CGFloat(self?.finallyUserAudioTime ?? 0)
|
|
|
+
|
|
|
+ self?.stuckPointCuttingView.stuckPointStartTime = CGFloat(CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero))
|
|
|
+ self?.stuckPointCuttingView.stuckPointEndTime = CGFloat(CMTimeGetSeconds(self?.playeTimeRange.end ?? .zero))
|
|
|
+ self?.stuckPointCuttingView.tatalTimeLabel.text = "\(Float64(CMTimeGetSeconds(self?.playeTimeRange.end ?? .zero) - CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero)).formatDurationToHMS())"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return stuckPointCuttingView
|
|
|
+ }()
|
|
|
+
|
|
|
+ /// 卡点模式标题
|
|
|
+ lazy var pointEditRemindLab: UILabel = {
|
|
|
+ let pointEditRemindLab = UILabel()
|
|
|
+ pointEditRemindLab.backgroundColor = .clear
|
|
|
+ pointEditRemindLab.textAlignment = .left
|
|
|
+ pointEditRemindLab.font = UIFont.boldSystemFont(ofSize: 14)
|
|
|
+ pointEditRemindLab.textColor = BFConfig.shared.styleTitleColor
|
|
|
+ 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 = BFConfig.shared.styleTitleColor
|
|
|
+ speedTitleLab.text = "节奏变化"
|
|
|
+ return speedTitleLab
|
|
|
+ }()
|
|
|
+
|
|
|
+ /// 卡点模式下方操作区背景
|
|
|
+ lazy var pointEditBGView: UIView = {
|
|
|
+ let pointEditBGView = UIView()
|
|
|
+ pointEditBGView.backgroundColor = .clear
|
|
|
+ return pointEditBGView
|
|
|
+ }()
|
|
|
+
|
|
|
+ /// 下方音乐编辑操作区背景
|
|
|
+ lazy var musicEditBGView: PQSelecteMusicView = {
|
|
|
+ let musicEditBGView = PQSelecteMusicView()
|
|
|
+ musicEditBGView.backgroundColor = .clear
|
|
|
+ musicEditBGView.isUserInteractionEnabled = true
|
|
|
+ musicEditBGView.isHidden = true
|
|
|
+ musicEditBGView.musicSearchBtn.addTarget(self, action: #selector(musicSearchBtnClick(sender:)), for: .touchUpInside)
|
|
|
+
|
|
|
+ musicEditBGView.didSelectItemHandle = { [weak self] status in
|
|
|
+ if status == .isSelected {
|
|
|
+ if self?.playerView.status != .playing {
|
|
|
+ self?.playerView.RenderViewOnclick()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ 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.moduleImage(named: "pointEditerBtn_n", moduleName: "BFFramework", isAssets: false), for: .normal)
|
|
|
+
|
|
|
+ pointEdterBtn.setImage(UIImage.moduleImage(named: "pointEditerBtn_h", moduleName: "BFFramework", isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .selected)
|
|
|
+ pointEdterBtn.tintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
|
|
|
+ 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.moduleImage(named: "musicEditerBtn_n", moduleName: "BFFramework", isAssets: false), for: .normal)
|
|
|
+ musicEditerBtn.setImage(UIImage.moduleImage(named: "musicEditerBtn_h", moduleName: "BFFramework", isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .selected)
|
|
|
+ musicEditerBtn.tintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
|
|
|
+ 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(editModelClick1(sender:)), for: .touchUpInside)
|
|
|
+ speedStuckBtn.setTitle("快慢速卡点", for: .normal)
|
|
|
+ speedStuckBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .regular)
|
|
|
+ jumpPointBtn.backgroundColor = BFConfig.shared.pointEditNamalBackgroundColor
|
|
|
+ speedStuckBtn.setTitleColor(UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue), for: .selected)
|
|
|
+ speedStuckBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#959595"), for: .normal)
|
|
|
+ speedStuckBtn.addCorner(corner: 5)
|
|
|
+ speedStuckBtn.imagePosition(at: .top, space: 8)
|
|
|
+ speedStuckBtn.tag = 1
|
|
|
+ speedStuckBtn.adjustsImageWhenHighlighted = false
|
|
|
+ speedStuckBtn.setImage(UIImage.moduleImage(named: BFConfig.shared.speedStuckBtnImage_N, moduleName: "BFFramework", isAssets: false), for: .normal)
|
|
|
+ speedStuckBtn.setImage(UIImage.moduleImage(named: BFConfig.shared.speedStuckBtnImage_H, moduleName: "BFFramework", isAssets: false), for: .selected)
|
|
|
+ return speedStuckBtn
|
|
|
+ }()
|
|
|
+
|
|
|
+// //
|
|
|
+// lazy var speedStuckBtnGif: UIImageView = {
|
|
|
+// let speedStuckBtnGif = UIImageView()
|
|
|
+// speedStuckBtnGif.kf.setImage(with: URL(fileURLWithPath: currentBundlePath()!.path(forResource: "speedstuck_h", ofType: "gif")!))
|
|
|
+// speedStuckBtnGif.isHidden = true
|
|
|
+// return speedStuckBtnGif
|
|
|
+//
|
|
|
+// }()
|
|
|
+
|
|
|
+ // 跳转卡点模式 btn
|
|
|
+ lazy var jumpPointBtn: UIButton = {
|
|
|
+ let jumpPointBtn = UIButton(type: .custom)
|
|
|
+
|
|
|
+ jumpPointBtn.setTitle("跳跃卡点", for: .normal)
|
|
|
+ jumpPointBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .regular)
|
|
|
+ jumpPointBtn.backgroundColor = BFConfig.shared.pointEditNamalBackgroundColor
|
|
|
+ jumpPointBtn.setTitleColor(UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue), for: .selected)
|
|
|
+ jumpPointBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#959595"), for: .normal)
|
|
|
+ jumpPointBtn.imagePosition(at: .top, space: 8)
|
|
|
+ jumpPointBtn.addCorner(corner: 5)
|
|
|
+ jumpPointBtn.tag = 2
|
|
|
+ jumpPointBtn.addTarget(self, action: #selector(editModelClick1(sender:)), for: .touchUpInside)
|
|
|
+ jumpPointBtn.adjustsImageWhenHighlighted = false
|
|
|
+ jumpPointBtn.setImage(UIImage.moduleImage(named: BFConfig.shared.jumpPointBtnImage_N, moduleName: "BFFramework", isAssets: false), for: .normal)
|
|
|
+ jumpPointBtn.setImage(UIImage.moduleImage(named: BFConfig.shared.jumpPointBtnImage_H, moduleName: "BFFramework", isAssets: false), for: .selected)
|
|
|
+ return jumpPointBtn
|
|
|
+ }()
|
|
|
+
|
|
|
+// lazy var jumpPointBtnGif: UIImageView = {
|
|
|
+// let jumpPointBtnGif = UIImageView()
|
|
|
+// jumpPointBtnGif.kf.setImage(with: URL(fileURLWithPath: currentBundlePath()!.path(forResource: "jumpPoint_n", ofType: "gif")!))
|
|
|
+// jumpPointBtnGif.isHidden = true
|
|
|
+// return jumpPointBtnGif
|
|
|
+//
|
|
|
+// }()
|
|
|
+
|
|
|
+ // 仅配乐模式 btn
|
|
|
+ lazy var onlyMusicBtn: UIButton = {
|
|
|
+ let onlyMusicBtn = UIButton(type: .custom)
|
|
|
+
|
|
|
+ onlyMusicBtn.setTitle("仅配乐", for: .normal)
|
|
|
+ onlyMusicBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .regular)
|
|
|
+ onlyMusicBtn.backgroundColor = BFConfig.shared.pointEditNamalBackgroundColor
|
|
|
+ onlyMusicBtn.setTitleColor(UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue), for: .selected)
|
|
|
+ onlyMusicBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#959595"), for: .normal)
|
|
|
+ onlyMusicBtn.addCorner(corner: 5)
|
|
|
+ onlyMusicBtn.tag = 3
|
|
|
+ onlyMusicBtn.addTarget(self, action: #selector(editModelClick1(sender:)), for: .touchUpInside)
|
|
|
+ onlyMusicBtn.adjustsImageWhenHighlighted = false
|
|
|
+ onlyMusicBtn.setImage(UIImage.moduleImage(named: BFConfig.shared.onlyMusicBtnImage_N, moduleName: "BFFramework", isAssets: false), for: .normal)
|
|
|
+ onlyMusicBtn.setImage(UIImage.moduleImage(named: BFConfig.shared.onlyMusicBtnImage_H, moduleName: "BFFramework", isAssets: false), for: .selected)
|
|
|
+ return onlyMusicBtn
|
|
|
+ }()
|
|
|
+
|
|
|
+ // 操作面板上的分割线
|
|
|
+ lazy var optionlineView: UIView = {
|
|
|
+ let optionlineView = UIView()
|
|
|
+ optionlineView.backgroundColor = BFConfig.shared.pointEditNamalBackgroundColor
|
|
|
+ return optionlineView
|
|
|
+ }()
|
|
|
+
|
|
|
+ // 固定速度 UI
|
|
|
+ lazy var speedSettingView: PQSpeedSettingView = {
|
|
|
+ let speedSetView = PQSpeedSettingView()
|
|
|
+ speedSetView.backgroundColor = .clear
|
|
|
+ speedSetView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed, selectIndex, isSettingPlayer in
|
|
|
+ BFLog(message: "固定maxSpeed is\(maxSpeed) minSpeed \(minSpeed)")
|
|
|
+ self?.musicEditBGView.pausePlayer()
|
|
|
+
|
|
|
+ if maxSpeed == -1.0, minSpeed == -1.0 {
|
|
|
+ self?.customSpeedSettingView.isHidden = false
|
|
|
+ self?.customSpeedSettingView.viewType = self?.speedSettingView.viewType ?? 2
|
|
|
+ } else {
|
|
|
+ if maxSpeed != 0.0 {
|
|
|
+ // 更新最后一次选择的位置恢复时使用
|
|
|
+ if self?.speedSettingView.viewType == 1 {
|
|
|
+ self?.lastSpeedSelectIndex = selectIndex
|
|
|
+ self?.modelSpeed_maxSpeed = maxSpeed
|
|
|
+ self?.modelSpeed_minSpeed = minSpeed
|
|
|
+ } else if self?.speedSettingView.viewType == 2 {
|
|
|
+ self?.lastJumpSpeedSelectIndex = selectIndex
|
|
|
+ self?.modelPoint_speed = maxSpeed
|
|
|
+ } else {
|
|
|
+ self?.lastCyclesSelectIndex = selectIndex
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ BFLog(message: "设置速度无效")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if isSettingPlayer {
|
|
|
+ self?.settingPlayerView()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return speedSetView
|
|
|
+
|
|
|
+ }()
|
|
|
+
|
|
|
+ // 自定义速度
|
|
|
+ lazy var customSpeedSettingView: PQCustomSpeedSettingView = {
|
|
|
+ let customSpeedSetView = PQCustomSpeedSettingView(frame: CGRect(x: 0, y: cScreenHeigth - 354, width: cScreenWidth, height: 354))
|
|
|
+ customSpeedSetView.isHidden = true
|
|
|
+ customSpeedSetView.selectSpeedCallBack = { [weak self, weak customSpeedSetView] maxSpeed, minSpeed, isJumpSpeedModel, isCancle in
|
|
|
+ if !isCancle {
|
|
|
+ BFLog(message: "自定义速度maxSpeed is\(maxSpeed) minSpeed \(minSpeed) \(isJumpSpeedModel)")
|
|
|
+ self?.musicEditBGView.pausePlayer()
|
|
|
+
|
|
|
+ // 自定定义的更新一下最后的选择位置
|
|
|
+ if self?.speedSettingView.viewType == 1 {
|
|
|
+ self?.lastSpeedSelectIndex = -1
|
|
|
+ self?.modelSpeed_maxSpeed = maxSpeed
|
|
|
+ self?.modelSpeed_minSpeed = minSpeed
|
|
|
+ } else if self?.speedSettingView.viewType == 2 {
|
|
|
+ self?.lastJumpSpeedSelectIndex = -1
|
|
|
+ self?.modelPoint_speed = maxSpeed
|
|
|
+ } else {
|
|
|
+ self?.lastCyclesSelectIndex = Int(maxSpeed - 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ self?.settingPlayerView()
|
|
|
+ // 确认后 选中自定义
|
|
|
+ self?.speedSettingView.selectCustom()
|
|
|
+ } else {
|
|
|
+ // 取消后恢复上一次选择的位置
|
|
|
+ if self?.speedSettingView.viewType == 1 {
|
|
|
+ self?.speedSettingView.setSelectItem(index: self?.lastSpeedSelectIndex ?? 0, isSettingPlayer: false)
|
|
|
+ } else if self?.speedSettingView.viewType == 2 {
|
|
|
+ self?.speedSettingView.setSelectItem(index: self?.lastJumpSpeedSelectIndex ?? 0, isSettingPlayer: false)
|
|
|
+ } else {
|
|
|
+ self?.speedSettingView.setSelectItem(index: self?.lastCyclesSelectIndex ?? 0, isSettingPlayer: false)
|
|
|
+ }
|
|
|
+
|
|
|
+ customSpeedSetView?.isHidden = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return customSpeedSetView
|
|
|
+
|
|
|
+ }()
|
|
|
+
|
|
|
+ /// 音乐标题
|
|
|
+ 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 = BFConfig.shared.styleTitleColor
|
|
|
+ musicImageView.image = UIImage.moduleImage(named: "stuckPoint_reCreate_music", moduleName: "BFFramework", isAssets: false)?.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 = BFConfig.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
|
|
|
+ }()
|
|
|
+
|
|
|
+ @objc func willEnterForeground() {
|
|
|
+ BFLog(message: "进入到前台")
|
|
|
+ if navigationController?.topViewController == self {
|
|
|
+ if projectModel.sData!.sections.count > 0 {
|
|
|
+ settingPlayerView()
|
|
|
+ } else {
|
|
|
+ prepareMeta()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func enterBackground() {
|
|
|
+ BFLog(message: "进入到后台")
|
|
|
+ // 取消导出
|
|
|
+ if navigationController?.topViewController == self {
|
|
|
+ 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(resetSelectIndex: false)
|
|
|
+
|
|
|
+ // 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 ?? "") "
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新一下节奏的 UI
|
|
|
+ sustomSwitchView.selectOneBtn(Index: stuckPointMusicData?.speed ?? 2)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 点击搜索音乐
|
|
|
+ @objc func musicSearchBtnClick(sender _: UIButton) {
|
|
|
+ let searchVC = PQEditMusicSearchController()
|
|
|
+
|
|
|
+ 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
|
|
|
+
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_videoTab, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 音乐编辑
|
|
|
+ @objc func musicEditerBtnClick(sender: UIButton) {
|
|
|
+ if sender.isSelected { return }
|
|
|
+ sender.isSelected = !sender.isSelected
|
|
|
+ pointEditerBtn.isSelected = false
|
|
|
+ pointEditBGView.isHidden = true
|
|
|
+
|
|
|
+ musicEditBGView.showData()
|
|
|
+
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_musicTab, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func editModelClick1(sender: UIButton) {
|
|
|
+ editModelClick(sender: sender)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 三种模式修改
|
|
|
+ @objc func editModelClick(sender: UIButton, reportLog: Bool = true) {
|
|
|
+// if sender.isSelected {
|
|
|
+// BFLog(message: "已经是选中状态")
|
|
|
+// return “”
|
|
|
+// }
|
|
|
+
|
|
|
+ musicEditBGView.pausePlayer()
|
|
|
+ sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
|
|
|
+
|
|
|
+ if sender == jumpPointBtn, selectedTotalDuration < 6, selectedDataCount != selectedImageDataCount, reCreateVideoData == nil {
|
|
|
+ cShowHUB(superView: view, msg: "素材时长需要大于6秒才\n可选择“跳跃卡点”模式")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ lastEditModelBtn?.isSelected = false
|
|
|
+ //设置取消选中的背景色
|
|
|
+ lastEditModelBtn?.backgroundColor = BFConfig.shared.pointEditNamalBackgroundColor
|
|
|
+ sender.isSelected = !sender.isSelected
|
|
|
+ lastEditModelBtn = sender
|
|
|
+ //设置选中的背景色
|
|
|
+ let styleColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
|
|
|
+ lastEditModelBtn?.backgroundColor = UIColor(red: styleColor.rgbaf[0], green: styleColor.rgbaf[1], blue: styleColor.rgbaf[2], alpha: 0.15)
|
|
|
+
|
|
|
+ BFLog(message: "sender tag is \(sender.tag)")
|
|
|
+ // 2素材全是图片的时候三个模式都显示循环设置 UI
|
|
|
+ if selectedDataCount == selectedImageDataCount {
|
|
|
+ speedSettingView.viewType = 3
|
|
|
+ customSpeedSettingView.viewType = speedSettingView.viewType
|
|
|
+ speedSettingView.snp.remakeConstraints { make in
|
|
|
+ make.left.equalToSuperview().offset(16)
|
|
|
+ make.right.equalToSuperview()
|
|
|
+ make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
|
|
|
+ make.height.equalTo(30)
|
|
|
+ }
|
|
|
+
|
|
|
+ speedSettingView.isHidden = false
|
|
|
+ speedTitleLab.isHidden = false
|
|
|
+ sustomSwitchView.isHidden = false
|
|
|
+ if lastCyclesSelectIndex != -1 {
|
|
|
+ speedSettingView.setSelectItem(index: lastCyclesSelectIndex, isSettingPlayer: false)
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // 1 ui 调整
|
|
|
+ if sender.tag == 1 || sender.tag == 2 {
|
|
|
+ speedSettingView.viewType = sender.tag
|
|
|
+ customSpeedSettingView.viewType = speedSettingView.viewType
|
|
|
+
|
|
|
+ speedSettingView.snp.remakeConstraints { make in
|
|
|
+ make.left.equalToSuperview().offset(16)
|
|
|
+ make.right.equalToSuperview()
|
|
|
+ make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
|
|
|
+ make.height.equalTo(sender.tag == 1 ? 44 : 30)
|
|
|
+ }
|
|
|
+ speedSettingView.isHidden = false
|
|
|
+ speedTitleLab.isHidden = false
|
|
|
+ sustomSwitchView.isHidden = false
|
|
|
+ if sender.tag == 1 { // 快慢速
|
|
|
+ speedSettingView.setSelectItem(index: lastSpeedSelectIndex, isSettingPlayer: false, setDisable: (selectedTotalDuration < 6 && selectedDataCount != selectedImageDataCount) ? true : false)
|
|
|
+ } else if sender.tag == 2 { // 跳跃卡点
|
|
|
+ speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex, isSettingPlayer: false)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ speedTitleLab.isHidden = true
|
|
|
+ speedSettingView.isHidden = true
|
|
|
+ sustomSwitchView.isHidden = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3 设置 btn 不同显示状态
|
|
|
+ var speedStuckBtnGifName = ""
|
|
|
+ var jumpPointBtnGifName = ""
|
|
|
+ if sender.tag == 1 { // 快慢速
|
|
|
+ speedStuckBtnGifName = "speedstuck_h_pq"
|
|
|
+ jumpPointBtnGifName = "jumpPoint_n_pq"
|
|
|
+ currentCreateStickersModel = .createStickersModelSpeed
|
|
|
+
|
|
|
+ if reportLog {
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectPatternSpeed, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if sender.tag == 2 { // 跳跃卡点
|
|
|
+ speedStuckBtnGifName = "speedstuck_n_pq"
|
|
|
+ jumpPointBtnGifName = "jumpPoint_h_pq"
|
|
|
+ currentCreateStickersModel = .createStickersModelPoint
|
|
|
+
|
|
|
+ if reportLog {
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectPatternMusicVideo, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if sender.tag == 3 { // 仅音乐
|
|
|
+ speedStuckBtnGifName = "speedstuck_n_pq"
|
|
|
+ jumpPointBtnGifName = "jumpPoint_n_pq"
|
|
|
+ currentCreateStickersModel = .createStickersModelOnlyMusic
|
|
|
+ if reportLog {
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_selectPatternBgm, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ settingPlayerView()
|
|
|
+ }
|
|
|
+
|
|
|
+ override func viewWillAppear(_ animated: Bool) {
|
|
|
+ super.viewWillAppear(animated)
|
|
|
+ lineView?.isHidden = true
|
|
|
+ DispatchQueue.main.async {
|
|
|
+ UIApplication.shared.isIdleTimerDisabled = true
|
|
|
+ }
|
|
|
+ musicNameLab.move()
|
|
|
+
|
|
|
+ PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
|
|
+ PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
|
|
|
+
|
|
|
+ // 从分享返回后从重初始化播放器
|
|
|
+ if isClickNextBtn {
|
|
|
+ isClickNextBtn = false
|
|
|
+ settingPlayerView()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override func viewWillDisappear(_ animated: Bool) {
|
|
|
+ super.viewWillDisappear(animated)
|
|
|
+ DispatchQueue.main.async {
|
|
|
+ UIApplication.shared.isIdleTimerDisabled = false
|
|
|
+ }
|
|
|
+ musicNameLab.stop()
|
|
|
+ playerView.stop()
|
|
|
+
|
|
|
+ musicEditBGView.pausePlayer()
|
|
|
+ PQNotification.removeObserver(self)
|
|
|
+
|
|
|
+ synchroMarskView.removeMarskView()
|
|
|
+ }
|
|
|
+
|
|
|
+ override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+ leftButton(image: nil, tintColor: BFConfig.shared.styleTitleColor)
|
|
|
+ navHeadImageView?.addSubview(nextBtn)
|
|
|
+ navHeadImageView?.addSubview(musicNameView)
|
|
|
+
|
|
|
+ // 添加子视图
|
|
|
+ addSubViews()
|
|
|
+
|
|
|
+ prepareMeta()
|
|
|
+
|
|
|
+ // 曝光上报:预览页面曝光上报
|
|
|
+ PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_previewSyncedUp, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:预览页面曝光上报)")
|
|
|
+
|
|
|
+ // 从选择的素材中 第一个素材设置封面
|
|
|
+ if selectedPhotoData != nil, selectedPhotoData!.count > 0 {
|
|
|
+ let photo = selectedPhotoData!.first!
|
|
|
+ let option = PHImageRequestOptions()
|
|
|
+ option.isNetworkAccessAllowed = true // 允许下载iCloud的图片
|
|
|
+ option.resizeMode = .none
|
|
|
+ option.deliveryMode = .highQualityFormat
|
|
|
+ PHImageManager.default().requestImage(for: photo, targetSize: CGSize(width: 1920, height: 1920), contentMode: .aspectFill, options: option) { [weak self] image, _ in
|
|
|
+ // image就是图片
|
|
|
+ if image != nil {
|
|
|
+ self?.firstFrameImage = image
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override func viewDidLayoutSubviews() {
|
|
|
+ super.viewDidLayoutSubviews()
|
|
|
+ playerView.resetCanvasFrame(frame: coculationPlayViewRect())
|
|
|
+ }
|
|
|
+
|
|
|
+ func prepareMeta() {
|
|
|
+ // 导出相册视频
|
|
|
+ exportPhotoData()
|
|
|
+ // 同步音乐数据
|
|
|
+ synchroMusicInfoData()
|
|
|
+
|
|
|
+ // 插入选择的音乐信息
|
|
|
+ musicEditBGView.firstInsertVoiceModel = stuckPointMusicData!
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 添加子视图
|
|
|
+ /// - 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)
|
|
|
+ pointEditBGView.addSubview(jumpPointBtn)
|
|
|
+ pointEditBGView.addSubview(onlyMusicBtn)
|
|
|
+ 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.equalToSuperview()
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新设置三个模式 btn 图片和title的位置
|
|
|
+ speedStuckBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(speedStuckBtn.imageView?.frame.size.width ?? 0), bottom: -(speedStuckBtn.imageView?.frame.size.height ?? 0), right: 0)
|
|
|
+ speedStuckBtn.imageEdgeInsets = UIEdgeInsets(top: -(speedStuckBtn.titleLabel?.intrinsicContentSize.height ?? 0), left: 0, bottom: 0, right: -(speedStuckBtn.titleLabel?.intrinsicContentSize.width ?? 0))
|
|
|
+
|
|
|
+ jumpPointBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(jumpPointBtn.imageView?.frame.size.width ?? 0), bottom: -(jumpPointBtn.imageView?.frame.size.height ?? 0), right: 0)
|
|
|
+ jumpPointBtn.imageEdgeInsets = UIEdgeInsets(top: -(jumpPointBtn.titleLabel?.intrinsicContentSize.height ?? 0), left: 0, bottom: 0, right: -(jumpPointBtn.titleLabel?.intrinsicContentSize.width ?? 0))
|
|
|
+
|
|
|
+ onlyMusicBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(onlyMusicBtn.imageView?.frame.size.width ?? 0), bottom: -(onlyMusicBtn.imageView?.frame.size.height ?? 0), right: 0)
|
|
|
+ onlyMusicBtn.imageEdgeInsets = UIEdgeInsets(top: -(onlyMusicBtn.titleLabel?.intrinsicContentSize.height ?? 0), left: 0, bottom: 0, right: -(onlyMusicBtn.titleLabel?.intrinsicContentSize.width ?? 0))
|
|
|
+
|
|
|
+ speedSettingView.snp.makeConstraints { make in
|
|
|
+ make.left.equalToSuperview().offset(16)
|
|
|
+ make.right.equalToSuperview()
|
|
|
+ make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
|
|
|
+ make.height.equalTo(44)
|
|
|
+ }
|
|
|
+ speedTitleLab.snp.makeConstraints { make in
|
|
|
+ make.left.equalToSuperview().offset(16)
|
|
|
+ make.top.equalToSuperview().offset(190)
|
|
|
+ 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: "去发布")
|
|
|
+
|
|
|
+ isClickNextBtn = true
|
|
|
+ playerView.pause()
|
|
|
+ // 使用深 copy
|
|
|
+ let json = projectModel.toJSONString(prettyPrint: false)
|
|
|
+
|
|
|
+ if json == nil {
|
|
|
+ BFLog(message: "数据转换有问题 跳转")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ let tempModel: PQEditProjectModel? = Mapper<PQEditProjectModel>().map(JSONString: json!)
|
|
|
+ let materialVC: PQStuckPointMaterialController? = navigationController?.viewControllers.first(where: { (vc) -> Bool in
|
|
|
+ vc is PQStuckPointMaterialController
|
|
|
+ }) as? PQStuckPointMaterialController
|
|
|
+
|
|
|
+ if materialVC != nil, materialVC?.isToPublicHandle != nil {
|
|
|
+ materialVC?.isToPublicHandle!(isReCreate, selectedTotalDuration, selectedDataCount, selectedImageDataCount, mStickers, stuckPointMusicData, tempModel, currentCreateStickersModel, modelSpeed_maxSpeed, modelSpeed_minSpeed, Float(finallyStuckPoints.last ?? 0) - Float(finallyStuckPoints.first ?? 0), getClipAudioRange(), playeTimeRange)
|
|
|
+ } else {
|
|
|
+ if finallyStuckPoints.count == 0 {
|
|
|
+ cShowHUB(superView: nil, msg: "无卡点信息,返回重新选择音乐")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ let videoExporter = PQStuckPointPublicController()
|
|
|
+ videoExporter.rhythmMode = currentCreateStickersModel
|
|
|
+ videoExporter.syncedUpVideoSpeedMin = modelSpeed_maxSpeed
|
|
|
+ videoExporter.syncedUpVideoSpeedMax = modelSpeed_minSpeed
|
|
|
+ videoExporter.isReCreate = isReCreate
|
|
|
+ videoExporter.selectedTotalDuration = selectedTotalDuration
|
|
|
+ videoExporter.selectedDataCount = selectedDataCount
|
|
|
+ videoExporter.selectedImageDataCount = selectedImageDataCount
|
|
|
+ videoExporter.finallyUserAudioTime = Float(finallyStuckPoints.last ?? 0) - Float(finallyStuckPoints.first ?? 0)
|
|
|
+ videoExporter.clipAudioRange = getClipAudioRange()
|
|
|
+ videoExporter.playeTimeRange = playeTimeRange
|
|
|
+ videoExporter.mStickers = mStickers
|
|
|
+ videoExporter.audioMixModel = stuckPointMusicData
|
|
|
+ videoExporter.coverImage = selectedMetarialData?.first?.coverImageUI ?? UIImage.init()
|
|
|
+ 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: "点击上报:去合成")
|
|
|
+ }
|
|
|
+
|
|
|
+ override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
|
+ super.touchesBegan(touches, with: event)
|
|
|
+ if touches.first?.view != customSpeedSettingView {
|
|
|
+ if !customSpeedSettingView.isHidden {
|
|
|
+ customSpeedSettingView.isHidden = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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,添加选择的视觉素材
|
|
|
+ if projectModel.sData?.sections.count == 0 {
|
|
|
+ let section: PQEditSectionModel = PQEditSectionModel()
|
|
|
+ selectedMetarialData?.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(model)
|
|
|
+ }
|
|
|
+ projectModel.sData?.sections.append(section)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2,添加背景音乐
|
|
|
+ projectModel.sData?.addBGM(audioMix: stuckPointMusicData!)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算拼接音乐的开始和结束点
|
|
|
+ func getClipAudioRange() -> CMTimeRange {
|
|
|
+ // 设置音乐的拼接范围,开始:推荐的卡点 结束:点到的倒数第二位
|
|
|
+ if stuckPointMusicData!.rhythmSdata.count > 0, stuckPointMusicData?.rhythmSdata[0].pointTimes.count ?? 0 > 2 {
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ return CMTimeRange(start: CMTime(value: 0, timescale: 1), duration: CMTime(value: 1, timescale: 1))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置播放器
|
|
|
+ func coculationPlayViewRect() -> CGRect {
|
|
|
+ let playerShowHeight = pointEditBGView.frame.minY - (navHeadImageView?.frame.maxY ?? 0)
|
|
|
+ var showRect: CGRect = CGRect(x: (cScreenWidth - playerShowHeight) / 2, y: 0, width: playerShowHeight, height: playerShowHeight)
|
|
|
+ if firstFrameImage != nil {
|
|
|
+ let w = firstFrameImage!.size.width
|
|
|
+ let h = firstFrameImage!.size.height
|
|
|
+ let ratioMaterial: Float = Float(w / max(h, 1))
|
|
|
+ if ratioMaterial > 1 {
|
|
|
+ // 横屏
|
|
|
+ let tempPlayerHeight = min(cScreenWidth * CGFloat(h / w), playerShowHeight)
|
|
|
+ showRect = CGRect(x: (cScreenWidth - tempPlayerHeight * CGFloat(ratioMaterial)) / 2, y: (playerShowHeight - tempPlayerHeight) / 2, width: tempPlayerHeight * CGFloat(ratioMaterial), height: tempPlayerHeight)
|
|
|
+ } else {
|
|
|
+ // 竖屏
|
|
|
+ let playerViewWidth = (CGFloat(w) / CGFloat(h)) * playerShowHeight
|
|
|
+ showRect = CGRect(x: (cScreenWidth - playerViewWidth) / 2, y: 0, width: playerViewWidth, height: 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
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if showRect.size.width != 0, showRect.size.height != 0 {
|
|
|
+ if Int(showRect.height) % 2 != 0 {
|
|
|
+ showRect.size.height = showRect.size.height + 1.0
|
|
|
+ }
|
|
|
+ if Int(showRect.width) % 2 != 0 {
|
|
|
+ showRect.size.width = showRect.size.width + 1.0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ showRect.origin.y = (playerShowHeight - showRect.size.height) / 2.0 + (navHeadImageView?.frame.maxY ?? 0)
|
|
|
+
|
|
|
+ return showRect
|
|
|
+ }
|
|
|
+
|
|
|
+ func settingPlayerView() {
|
|
|
+ stuckPointCuttingView.resetDefaultsColor()
|
|
|
+ synchroMarskView.show()
|
|
|
+ // 1,设置播放器的显示区域 和画布大小
|
|
|
+ // - 按第一个素材尺寸自适应
|
|
|
+ playerView.pause()
|
|
|
+
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ if videoSize.width.isNaN || videoSize.height.isNaN {
|
|
|
+ BFLog(1, message: "宽高无效NaN")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+
|
|
|
+ let beginTime = Date()
|
|
|
+ dealParameter(model: currentCreateStickersModel)
|
|
|
+
|
|
|
+ // 更新裁剪时间条的的ui数据
|
|
|
+ stuckPointCuttingView.videoDuration = max(CGFloat(finallyUserAudioTime), CGFloat(finallyStuckPoints.last!))
|
|
|
+ let counn = (stuckPointMusicData?.rhythmSdata[0].pointTimes.count)! - 2
|
|
|
+ let suggestRhythmStartTime = CGFloat(stuckPointMusicData?.suggestRhythmStartTime ?? 0)
|
|
|
+ let suggestRhythmEndTime = max(suggestRhythmStartTime, CGFloat(stuckPointMusicData?.rhythmSdata[0].pointTimes[max(counn, 0)] ?? 0) / CGFloat(BASE_FILTER_TIMESCALE))
|
|
|
+ stuckPointCuttingView.updateEndTime(
|
|
|
+ startTime: CGFloat(CMTimeGetSeconds(playeTimeRange.start)),
|
|
|
+ endTime: CGFloat(CMTimeGetSeconds(playeTimeRange.end)),
|
|
|
+ suggestRhythmStartTime: suggestRhythmStartTime,
|
|
|
+ suggestRhythmEndTime: suggestRhythmEndTime
|
|
|
+ )
|
|
|
+
|
|
|
+ // 2,创建滤镜
|
|
|
+ DispatchQueue.global().async {
|
|
|
+ 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)), model: self.currentCreateStickersModel)
|
|
|
+ DispatchQueue.main.async { // 串行、异步
|
|
|
+ self.playerView.mStickers = self.mStickers
|
|
|
+
|
|
|
+ BFLog(message: "createStickers tiskskskskme \(Date().timeIntervalSince(beginTime))")
|
|
|
+
|
|
|
+ // 3,设置音频
|
|
|
+ let audioPath = self.stuckPointMusicData?.localPath ?? ""
|
|
|
+
|
|
|
+ BFLog(message: "初始化音频播放器的音频地址为:\(audioPath)")
|
|
|
+ self.playerView.stop()
|
|
|
+ // 这里的测试这个音乐播放有问题
|
|
|
+ // self.playerView.updateAsset(URL(fileURLWithPath: "63930549652d74e477141e3b79c8d29a9ef8af81625053214516.mp3", relativeTo:Bundle.main.resourceURL!), videoComposition: nil, audioMixModel: nil)
|
|
|
+
|
|
|
+ self.playerView.updateAsset(URL(fileURLWithPath: documensDirectory + audioPath), videoComposition: nil, audioMixModel: nil, originMusicDuration: self.finallyUserAudioTime, clipAudioRange: self.getClipAudioRange(), isUsedAVPlayer: true)
|
|
|
+
|
|
|
+ // 4, 设置播放器的输出画布大小
|
|
|
+ self.playerView.movie?.mShowVidoSize = CGSize(width: CGFloat(self.projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(self.projectModel.sData?.videoMetaData?.videoHeight ?? 0))
|
|
|
+
|
|
|
+ // 传给movie 音频的原始卡点
|
|
|
+ let fir = Int64(self.stuckPointsTemp.first ?? 0)
|
|
|
+ let endd = Int64(self.stuckPointsTemp.last ?? 0)
|
|
|
+ self.playerView.movie?.orginStuckRange = CMTimeRange(start: CMTime(value: Int64(fir) * Int64(BASE_FILTER_TIMESCALE), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: Int64(endd) * Int64(BASE_FILTER_TIMESCALE), timescale: BASE_FILTER_TIMESCALE))
|
|
|
+
|
|
|
+ // 5,开始播放
|
|
|
+ self.playerView.isLoop = false
|
|
|
+ self.playerView.showProgressLab = true
|
|
|
+
|
|
|
+ // 初始化音频的开始和结束时间
|
|
|
+ BFLog(message: "播放的器 开始\(String(describing: CMTimeGetSeconds(self.playeTimeRange.start))) 结束 \(String(describing: CMTimeGetSeconds(self.playeTimeRange.end)))")
|
|
|
+ let end3: TimeInterval = Date().timeIntervalSince1970
|
|
|
+
|
|
|
+ self.playerView.play(pauseFirstFrame: false, playeTimeRange: CMTimeRange(start: self.playeTimeRange.start, end: self.playeTimeRange.end))
|
|
|
+ self.stuckPointCuttingView.updateProgress(progress: 0)
|
|
|
+ self.synchroMarskView.removeMarskView()
|
|
|
+
|
|
|
+ let end4: TimeInterval = Date().timeIntervalSince1970
|
|
|
+ BFLog(message: " playerView.play tiskskskskme \(end4 - end3)")
|
|
|
+
|
|
|
+ // 6,进度回调
|
|
|
+ self.playerView.progress = { [weak self] currentTime, tatolTime, percent in
|
|
|
+ if percent == 1 {
|
|
|
+ self?.stuckPointCuttingView.resetDefaultsColor(clearData: false)
|
|
|
+ sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero) <= 0.0 {
|
|
|
+ BFLog(message: "时长错误!!!!")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 更新进度
|
|
|
+ let progress = (currentTime - CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero)) / CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero)
|
|
|
+ BFLog(message: "\(currentTime) \(tatolTime) 显示播放器进度为: \(progress)")
|
|
|
+
|
|
|
+ self?.stuckPointCuttingView.updateProgress(progress: CGFloat(progress))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ deinit {
|
|
|
+ musicNameLab.stop()
|
|
|
+// playerView.pause()
|
|
|
+ // 取消所有的导出
|
|
|
+ PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
|
|
|
+ exportSession.cancelExport()
|
|
|
+ }
|
|
|
+ self.synchroMarskView.removeMarskView()
|
|
|
+ sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
|
|
|
+ BFLog(1, message: "卡点视频预览界面release")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 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 - 1) - selectedImageDataCount
|
|
|
+
|
|
|
+ // 所有视频总时长
|
|
|
+ var videoTotalDuration: Float64 = 0.0
|
|
|
+ for video in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video") {
|
|
|
+ // MARK: SanW-2021.11.15-不再导出到沙盒,直接使用本地相册地址
|
|
|
+
|
|
|
+ let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: video.locationPath), options: nil)
|
|
|
+// 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 {
|
|
|
+ // MARK: SanW-2021.11.15-不再导出到沙盒,直接使用本地相册地址
|
|
|
+
|
|
|
+ let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: sticker.locationPath), options: nil)
|
|
|
+// 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 {
|
|
|
+ // deep copy sticker model 防止只有一个对象
|
|
|
+ let deepCopySticker: PQEditVisionTrackMaterialsModel? = sticker.copy() as? PQEditVisionTrackMaterialsModel
|
|
|
+
|
|
|
+ // 设置循环模式和适配模式
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+// kongduan = clipNumTep
|
|
|
+ return stickers
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新 playeTimeRange & finallyUserAudioTime
|
|
|
+ func updateTimeInfomation() {
|
|
|
+ // 四,背景音乐时长处理)计算最后使用的音频时长, 如果不用拼接音频时长度是卡点的倒数第二位时间
|
|
|
+ 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(Float64(finallyStuckPoints.first ?? 0) * Float64(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue(Float64(finallyStuckPoints.last ?? 0) * Float64(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
|
|
|
+
|
|
|
+ for (index, usePoint) in finallyStuckPoints.enumerated() {
|
|
|
+ BFLog(message: "测试人员最后使用的卡点信息 \(index) : \(usePoint)")
|
|
|
+ }
|
|
|
+
|
|
|
+ BFLog(message: "计算后给播放器使用的开始:\(CMTimeGetSeconds(playeTimeRange.start)) 结束时间\(CMTimeGetSeconds(playeTimeRange.end)) 播放总时长:\(CMTimeGetSeconds(playeTimeRange.end) - CMTimeGetSeconds(playeTimeRange.start))")
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 创建sticker
|
|
|
+ /// - Parameters:
|
|
|
+ /// - sections: 项目所有段落数据信息
|
|
|
+ /// - inputSize: 画布大小
|
|
|
+ /// - Returns: filters 数据 播放器可直接使用
|
|
|
+ func createStickers(sections: List<PQEditSectionModel>, inputSize _: CGSize = .zero, model: createStickersModel = .createStickersModelPoint) -> [PQEditVisionTrackMaterialsModel] {
|
|
|
+ // 推荐卡点数
|
|
|
+ let beginDecoderTime: TimeInterval = Date().timeIntervalSince1970
|
|
|
+
|
|
|
+ // 保存滤镜对象数据
|
|
|
+ var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
|
|
|
+ for section in sections {
|
|
|
+ if section.sectionType == "normal" {
|
|
|
+ // 第一种情况:全是图片,三种模式都进行图片回环播放
|
|
|
+ if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
|
|
|
+ for (index, _) in finallyStuckPoints.enumerated() {
|
|
|
+ let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
|
|
|
+ BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
|
|
|
+//
|
|
|
+ let deepCopySticker: PQEditVisionTrackMaterialsModel? = sticker.copy() as? PQEditVisionTrackMaterialsModel
|
|
|
+
|
|
|
+ if deepCopySticker!.type == StickerType.IMAGE.rawValue {
|
|
|
+ if index + 1 < finallyStuckPoints.count {
|
|
|
+ deepCopySticker!.timelineIn = Float64(String(format: "%.6f", finallyStuckPoints[index])) ?? 0.0
|
|
|
+
|
|
|
+ deepCopySticker!.timelineOut = Float64(String(format: "%.6f", finallyStuckPoints[index + 1])) ?? 0.0
|
|
|
+ if deepCopySticker != nil {
|
|
|
+ deepCopySticker?.generateDefaultValues()
|
|
|
+ stickers.append(deepCopySticker!)
|
|
|
+
|
|
|
+ BFLog(1, message: "测试人员index is 纯图片 timelineOut:\(deepCopySticker!.timelineIn) timelineOut :\(deepCopySticker!.timelineOut)")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else { // 第二种情况:视频 + 图片情况、视频要分段+图片按卡点时长创建
|
|
|
+ if model == .createStickersModelPoint { // 跳跃卡点
|
|
|
+ // 第二种情况:有视频要进行分割
|
|
|
+ let clipFilters = clipVideoMerage(section: section, stuckPoints: finallyStuckPoints)
|
|
|
+ // 数据不一致时要对数据进行二次处理,不是最好方案
|
|
|
+ if clipFilters.count > finallyStuckPoints.count - 1 {
|
|
|
+ clipPoint(clipNum: clipFilters.count - finallyStuckPoints.count, oldPoints: finallyStuckPoints)
|
|
|
+
|
|
|
+ } else if clipFilters.count < finallyStuckPoints.count - 1 {
|
|
|
+ while clipFilters.count < finallyStuckPoints.count - 1 {
|
|
|
+ finallyStuckPoints.removeLast()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 更新最终使用值
|
|
|
+ updateTimeInfomation()
|
|
|
+ // stikcer段数比clipFilters 数 大于 1才是正确的
|
|
|
+ BFLog(message: "stikcer count is\(clipFilters.count) finallyStuckPoints count is\(finallyStuckPoints.count)")
|
|
|
+ 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]
|
|
|
+
|
|
|
+ if sticker.type == StickerType.IMAGE.rawValue {
|
|
|
+ BFLog(message: "当前是image filter !!!!!")
|
|
|
+ }
|
|
|
+ sticker.timelineIn = Float64(String(format: "%.6f", finallyStuckPoints[index])) ?? 0.0
|
|
|
+
|
|
|
+ sticker.timelineOut = Float64(String(format: "%.6f", 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
|
|
|
+ }
|
|
|
+ print("跳跃卡点测试人员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 { // 仅音乐 和 快慢速卡点
|
|
|
+ BFLog(message: "stuckPoints count is \(finallyStuckPoints.count)")
|
|
|
+
|
|
|
+ for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
|
|
|
+ if sticker.type == StickerType.VIDEO.rawValue {
|
|
|
+ // MARK: SanW-2021.11.15-不再导出到沙盒,直接使用本地相册地址
|
|
|
+
|
|
|
+ let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: sticker.locationPath), options: nil)
|
|
|
+// let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
|
|
|
+ BFLog(message: "单个视频\(sticker.locationPath)时长::\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(sticker.clipCount)")
|
|
|
+ var lastOutTime: Float64 = 0.0
|
|
|
+ for _ in 1 ... sticker.clipCount {
|
|
|
+ // deep copy sticker model 防止只有一个对象
|
|
|
+ let deepCopyStickerDecoderTime: TimeInterval = Date().timeIntervalSince1970
|
|
|
+
|
|
|
+ let deepCopySticker: PQEditVisionTrackMaterialsModel? = sticker.copy() as? PQEditVisionTrackMaterialsModel
|
|
|
+
|
|
|
+ BFLog(message: "生成stickers 总时长为 aaa\(Date().timeIntervalSince1970 - deepCopyStickerDecoderTime)")
|
|
|
+ // 设置循环模式和适配模式
|
|
|
+ deepCopySticker?.generateDefaultValues()
|
|
|
+ deepCopySticker?.materialDurationFit?.fitType = adapterMode.staticFrame.rawValue
|
|
|
+ // 当前分段的速度
|
|
|
+ var tempSpeed: Float = 1.0
|
|
|
+ if model == .createStickersModelSpeed {
|
|
|
+ tempSpeed = (stickers.count % 2) == 0 ? modelSpeed_maxSpeed : modelSpeed_minSpeed
|
|
|
+ }
|
|
|
+
|
|
|
+ if stickers.count + 1 < finallyStuckPoints.count {
|
|
|
+ deepCopySticker?.speedRate = tempSpeed
|
|
|
+ // 定义临时使用的变量
|
|
|
+ // 素材显示的开始时间和结束时间
|
|
|
+ let tempTimelineIn: Float64 = Float64(String(format: "%.6f", finallyStuckPoints[stickers.count])) ?? 0.0
|
|
|
+
|
|
|
+ let timelineOut: Float64 = Float64(String(format: "%.6f", finallyStuckPoints[stickers.count + 1])) ?? 0.0
|
|
|
+
|
|
|
+ // 素材分割的开始时间和结束时间
|
|
|
+ let tempModel_In = lastOutTime
|
|
|
+ var tempOut = lastOutTime + Float64(tempSpeed) * (timelineOut - tempTimelineIn)
|
|
|
+
|
|
|
+ // 处理最后一点视频素材不够卡点时长 e.g. 0.3 卡点时长0.5
|
|
|
+ if tempOut > CMTimeGetSeconds(asset.duration) {
|
|
|
+ // 最后一点素材时长
|
|
|
+ let lastAssetDuration = CMTimeGetSeconds(asset.duration) - lastOutTime
|
|
|
+ let pointDuration = timelineOut - tempTimelineIn
|
|
|
+ // 要适应到卡点内要使用的C速度
|
|
|
+ let needSpeed = lastAssetDuration / pointDuration
|
|
|
+ // 当前卡点段为快速 快速都用 C 速处理
|
|
|
+ BFLog(message: "最后一点视频素材不够卡点时长要做变速C处理 差\(tempOut - CMTimeGetSeconds(asset.duration)) \(needSpeed)")
|
|
|
+ deepCopySticker?.speedRate = Float(needSpeed)
|
|
|
+ tempOut = CMTimeGetSeconds(asset.duration)
|
|
|
+ if needSpeed == 0 {
|
|
|
+ BFLog(message: "needSpeed is 0 出现在时长和卡点正好相等")
|
|
|
+ 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)) clipNum \(sticker.clipCount) timelineIN: \(String(format: "%.6f", deepCopySticker?.timelineIn ?? 0)) timelineOUT:\(String(format: "%.6f", deepCopySticker?.timelineOut ?? 0)) speedRate:\(deepCopySticker?.speedRate ?? 0.0)")
|
|
|
+
|
|
|
+ if deepCopySticker != nil {
|
|
|
+ if deepCopySticker?.timelineIn == 0 {
|
|
|
+ BFLog(message: "timelineIn data is error!!!")
|
|
|
+ }
|
|
|
+ stickers.append(deepCopySticker!)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if sticker.type == StickerType.IMAGE.rawValue {
|
|
|
+ if stickers.count + 1 >= finallyStuckPoints.count {
|
|
|
+ BFLog(message: "数据出现错误!!!查正") // 155.318253
|
|
|
+ break
|
|
|
+ }
|
|
|
+ sticker.generateDefaultValues()
|
|
|
+
|
|
|
+ sticker.timelineIn = Float64(String(format: "%.6f", finallyStuckPoints[stickers.count])) ?? 0.0
|
|
|
+ sticker.timelineOut = Float64(String(format: "%.6f", finallyStuckPoints[stickers.count + 1])) ?? 0.0
|
|
|
+ stickers.append(sticker)
|
|
|
+ BFLog(message: "测试人员创建 sticker crilp is in 图片 \(String(format: "%.6f", sticker.model_in)) out \(String(format: "%.6f", sticker.out)) ,分段素材时长:\(String(format: "%.6f", sticker.out - sticker.model_in)) ,分段显示时长:\(String(format: "%.6f", sticker.timelineOut - sticker.timelineIn)), timelineIN: \(String(format: "%.6f", sticker.timelineIn)) timelineOUT:\(String(format: "%.6f", sticker.timelineOut)) speedRate:\(sticker.speedRate)")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ BFLog(message: "生成stickers 总时长为:\(Date().timeIntervalSince1970 - beginDecoderTime)")
|
|
|
+ return stickers
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 根据档位取最后使用的卡点数据
|
|
|
+ /// - Parameter seed: 档位速度
|
|
|
+ /// - Returns: 最后使用的卡点
|
|
|
+ func getUsedStuckPoint(seed: Int) -> [Float] {
|
|
|
+ if !(stuckPointMusicData!.rhythmSdata.count > 0 && stuckPointMusicData!.rhythmSdata[0].pointTimes.count > 1) {
|
|
|
+ return []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 推荐卡点数
|
|
|
+ var stuckPoints: Array = Array<Float>.init()
|
|
|
+
|
|
|
+ var pointsTemp = Array<Float>.init()
|
|
|
+ //
|
|
|
+ // 最后一个卡点时间(原推荐卡点的倒数第二位时间)
|
|
|
+ let lastPoint = stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2]
|
|
|
+
|
|
|
+ for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
|
|
|
+ if dunshu >= Int64((stuckPointMusicData?.startTime ?? 0) * Float64(BASE_FILTER_TIMESCALE)), dunshu < lastPoint {
|
|
|
+ let savePointStr = String(format: "%.6f", Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
|
|
|
+ BFLog(message: "原所有卡点数:\(index) \(savePointStr)")
|
|
|
+
|
|
|
+ pointsTemp.append(Float(savePointStr) ?? 0.0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ 一,快慢速模式下取卡点 2 3 4
|
|
|
+ 二,跳跃卡点模式下根据不同速度 取卡点 1,2,3
|
|
|
+ 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
|
|
|
+ 适中为每两个点位取一个,即0,2,4,6……
|
|
|
+ 慢节奏为每三个点位取一个,即0,3,6,9……
|
|
|
+ */
|
|
|
+ // 不丢
|
|
|
+ if seed == 1 {
|
|
|
+ stuckPoints = pointsTemp
|
|
|
+ } else {
|
|
|
+ // 根据档位要丢
|
|
|
+ for (index, point) in pointsTemp.enumerated() {
|
|
|
+ if index % seed == 0 {
|
|
|
+ stuckPoints.append(point)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// for point in stuckPoints {
|
|
|
+// BFLog(message: "没有 start end 计算后的卡点数\(point)")
|
|
|
+// }
|
|
|
+ // 若音乐起点至第一个卡点点位之间时长t0<0.3时,此段时长与下一个点位时长合并,故第一段卡点部分时长为t0+d
|
|
|
+ while (stuckPoints.first ?? 0.0) - Float(stuckPointMusicData?.startTime ?? 0) < 0.3 {
|
|
|
+ if stuckPoints.first != nil {
|
|
|
+ stuckPoints.removeFirst()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stuckPoints.insert(Float(stuckPointMusicData?.startTime ?? 0), at: 0)
|
|
|
+
|
|
|
+// for point in stuckPoints {
|
|
|
+// BFLog(message: "有 start end 计算后的卡点数\(point)")
|
|
|
+// }
|
|
|
+
|
|
|
+ BFLog(message: "处理节奏后 stuckPoints count is \(stuckPoints.count) seed \(seed), start time:\(stuckPoints.first ?? 0.0),end time:\(stuckPoints.last ?? 0.0) 总时长为:\((stuckPoints.last ?? 0.0) - (stuckPoints.first ?? 0.0))")
|
|
|
+
|
|
|
+ return stuckPoints
|
|
|
+ }
|
|
|
+
|
|
|
+ func clipPoint(clipNum: Int, oldPoints: [Float]) {
|
|
|
+ BFLog(message: "拼接卡点数:\(clipNum)")
|
|
|
+ if clipNum < 0 || oldPoints.count < 2 {
|
|
|
+ BFLog(message: "clipNum is error!!!! \(clipNum)")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 如果是第一次拼接先补第0位
|
|
|
+ if finallyStuckPoints.count == 0 {
|
|
|
+ finallyStuckPoints.append(stuckPointsTemp.first ?? 0.0)
|
|
|
+ }
|
|
|
+
|
|
|
+ for i in finallyStuckPoints.count ... clipNum + finallyStuckPoints.count {
|
|
|
+ if (i % (oldPoints.count - 1)) != 1 {
|
|
|
+ let value = String(format: "%.6f", finallyStuckPoints[i - 1] + oldPoints[((i - 1) % (oldPoints.count - 1)) + 1] - oldPoints[((i - 2) % (oldPoints.count - 1)) + 1])
|
|
|
+
|
|
|
+ finallyStuckPoints.append(Float(value) ?? 0.0)
|
|
|
+ } else {
|
|
|
+ let value = String(format: "%.6f", finallyStuckPoints[i - 1] + oldPoints[1] - oldPoints[0])
|
|
|
+ finallyStuckPoints.append(Float(value) ?? 0.0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 根据不同模式model, maxSpeed ,minSpeed, self?.stuckPointMusicData?.speed 档位,生成音乐时长和最终使用的卡点信息
|
|
|
+ func dealParameter(model: createStickersModel) {
|
|
|
+ // 清空上一次使用的卡点数据
|
|
|
+ finallyStuckPoints.removeAll()
|
|
|
+
|
|
|
+ // 已经取到的视频素材总长度,用于和原视频素材时长做对比,不够多加一个点
|
|
|
+ var useAssestDuration: Float = 0.0
|
|
|
+ switch model {
|
|
|
+ case .createStickersModelPoint: // 跳跃卡点
|
|
|
+ stuckPointsTemp = getUsedStuckPoint(seed: stuckPointMusicData?.speed ?? 0)
|
|
|
+
|
|
|
+ // 要拼接的段数
|
|
|
+ var clipNum: Int = 0
|
|
|
+
|
|
|
+ var i: Int = 0
|
|
|
+ // L/(n+1) L -原视觉素材总时长 n-抛留倍数 lastJumpSpeedSelectIndex 是位置 对应的值要+1
|
|
|
+ // 根据公式计划出的总时长
|
|
|
+ let jumpTime = Float(selectedTotalDuration) / Float(modelPoint_speed + 1)
|
|
|
+ // 只有图片素材时会为0
|
|
|
+ if jumpTime > 0 {
|
|
|
+ while useAssestDuration < Float(jumpTime) {
|
|
|
+ // 回环从头取\
|
|
|
+ if i + 1 >= stuckPointsTemp.count {
|
|
|
+ i = 0
|
|
|
+ }
|
|
|
+ // 快速段
|
|
|
+ let LA = (stuckPointsTemp[i + 1] - stuckPointsTemp[i])
|
|
|
+ useAssestDuration = useAssestDuration + Float(LA)
|
|
|
+
|
|
|
+ i = i + 1
|
|
|
+ clipNum = clipNum + 1
|
|
|
+ }
|
|
|
+ // 拼接要使用的卡点信息
|
|
|
+ clipPoint(clipNum: clipNum, oldPoints: stuckPointsTemp)
|
|
|
+ }
|
|
|
+
|
|
|
+ case .createStickersModelSpeed, .createStickersModelOnlyMusic: // 快慢速
|
|
|
+ // 快慢速 (2:快节奏,3:适中,4:慢节奏)
|
|
|
+ var tempMaxSpeed: Float = 1
|
|
|
+ var tempMinSpeed: Float = 1
|
|
|
+ if model == .createStickersModelSpeed {
|
|
|
+ // 改变速率,.只有快慢速且非只有图片素材时自动+1处理
|
|
|
+ if model == .createStickersModelSpeed, selectedDataCount != selectedImageDataCount {
|
|
|
+ stuckPointsTemp = getUsedStuckPoint(seed: (stuckPointMusicData?.speed ?? 0) + 1)
|
|
|
+ } else {
|
|
|
+ stuckPointsTemp = getUsedStuckPoint(seed: stuckPointMusicData?.speed ?? 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ tempMaxSpeed = modelSpeed_maxSpeed
|
|
|
+ tempMinSpeed = modelSpeed_minSpeed
|
|
|
+ } else {
|
|
|
+ stuckPointsTemp = getUsedStuckPoint(seed: stuckPointMusicData?.speed ?? 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ - 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快速为:\(tempMaxSpeed) By慢速为:\(tempMinSpeed) 档位 N为:\(stuckPointMusicData?.speed ?? 0) 使用的卡点总数:\(stuckPointsTemp.count)")
|
|
|
+ // 使用新方法取使用的卡点数据
|
|
|
+ for section in projectModel.sData?.sections ?? List() {
|
|
|
+ if section.sectionType == "normal" {
|
|
|
+ for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
|
|
|
+ if sticker.type == StickerType.VIDEO.rawValue {
|
|
|
+ // MARK: SanW-2021.11.15-不再导出到沙盒,直接使用本地相册地址
|
|
|
+
|
|
|
+ let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: sticker.locationPath), options: nil)
|
|
|
+// let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
|
|
|
+ let assetDuration = Float(CMTimeGetSeconds(asset.duration))
|
|
|
+ BFLog(message: "输入素材时长 \(assetDuration)")
|
|
|
+
|
|
|
+ if finallyStuckPoints.count == 0 {
|
|
|
+ finallyStuckPoints.append(stuckPointsTemp[0])
|
|
|
+ }
|
|
|
+ var j = finallyStuckPoints.count
|
|
|
+ // 添加卡点的段数 用于判断当前卡点是快/慢速
|
|
|
+ var pointCount: Int = 1
|
|
|
+ // 已经取到的视频素材总长度,用于和原视频素材时长做对比,不够多加一个卡点
|
|
|
+ var useAssestDurationTemp: Float = 0.0
|
|
|
+ while useAssestDurationTemp < assetDuration {
|
|
|
+ // 当前段的应该使用的速度 A/B
|
|
|
+ let useSpeed = (pointCount % 2 != 0) ? tempMaxSpeed : tempMinSpeed
|
|
|
+ // 计算卡点
|
|
|
+ // 要添加的卡点数值
|
|
|
+ var sub: Float = 0.0
|
|
|
+ if stuckPointsTemp.count > 2 {
|
|
|
+ if ((j - 1) % (stuckPointsTemp.count - 1)) != 0 {
|
|
|
+ sub = stuckPointsTemp[((j - 1) % (stuckPointsTemp.count - 1)) + 1] - stuckPointsTemp[(j - 1) % (stuckPointsTemp.count - 1)]
|
|
|
+ } else {
|
|
|
+ sub = stuckPointsTemp[1] - stuckPointsTemp[0]
|
|
|
+ }
|
|
|
+
|
|
|
+ finallyStuckPoints.append(finallyStuckPoints[j - 1] + sub)
|
|
|
+ j += 1
|
|
|
+ }
|
|
|
+ useAssestDurationTemp += sub * useSpeed
|
|
|
+ if useAssestDurationTemp > assetDuration {
|
|
|
+ useAssestDurationTemp -= sub * useSpeed
|
|
|
+ break
|
|
|
+ }
|
|
|
+ pointCount += 1
|
|
|
+ BFLog(2, message: "finallyStuckPoints last ;\((finallyStuckPoints.last ?? 0.0) - (finallyStuckPoints.first ?? 0.0)), tmp:\(useAssestDurationTemp)")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2拼接要使用的卡点信息
|
|
|
+ sticker.clipCount = pointCount
|
|
|
+ if stuckPointsTemp.count < 1 {
|
|
|
+ // todo 和产品沟通提示
|
|
|
+ BFLog(message: "卡点数据有错误!!!")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ BFLog(message: "finallyStuckPoints\(finallyStuckPoints)")
|
|
|
+
|
|
|
+ // 3,多补一个卡点 做 C级 速处理,要根据条件不满足 要删除最后一位,
|
|
|
+ if useAssestDurationTemp < assetDuration {
|
|
|
+ /*
|
|
|
+ // 下一个卡的的速度性质快、慢,e.g. sticker.clipCount = 5时 tempSpeed 应该是慢速。下面计算正确。
|
|
|
+ var tempSpeed: Float = 1.0
|
|
|
+ if model == .createStickersModelSpeed {
|
|
|
+ tempSpeed = (sticker.clipCount) % 2 != 0 ? modelSpeed_maxSpeed : modelSpeed_minSpeed
|
|
|
+ }
|
|
|
+ // 最后一点素材时长
|
|
|
+ let lastAssetDuration = Float(CMTimeGetSeconds(asset.duration)) - useAssestDurationTemp
|
|
|
+
|
|
|
+ let lastPointIndex = (sticker.clipCount % stuckPointsTemp.count)
|
|
|
+ // 两个卡点
|
|
|
+ let a: Float = stuckPointsTemp[lastPointIndex]
|
|
|
+ var b: Float = 0.0
|
|
|
+ if lastPointIndex + 1 < stuckPointsTemp.count {
|
|
|
+ b = stuckPointsTemp[lastPointIndex + 1]
|
|
|
+ let pointDuration = b - a
|
|
|
+ // 要适应到卡点内要使用的C速度
|
|
|
+ let needSpeed = lastAssetDuration / pointDuration
|
|
|
+ // 当前卡点段为快速
|
|
|
+ if tempSpeed >= 1 {
|
|
|
+ // if needSpeed < 0.4 * tempSpeed {
|
|
|
+ // BFLog(message: "条件不满足不用补位 删除多加的一位")
|
|
|
+ // finallyStuckPoints.removeLast()
|
|
|
+ // }
|
|
|
+ } else { // 当前卡点段为慢速
|
|
|
+ if needSpeed >= 0.4 * tempSpeed && needSpeed >= 0.2 {
|
|
|
+ // 查找使用的最后一个卡点在原数组中的位置
|
|
|
+ } else {
|
|
|
+ BFLog(message: "条件不满足不用补位 删除多加的一位")
|
|
|
+ finallyStuckPoints.removeLast()
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ */
|
|
|
+ } else {
|
|
|
+ // 出现在第一个卡点X 倍速 > 原素材
|
|
|
+ finallyStuckPoints.removeLast()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 拼接图片所使用的时长.选择一组图片 按图片数量计算卡点的总时长
|
|
|
+ if selectedImageDataCount > 0 {
|
|
|
+ clipPoint(clipNum: selectedImageDataCount - 1, oldPoints: stuckPointsTemp)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 全是图片时数组里放着的一定都是图片的使用的卡点
|
|
|
+ // 定义一次循环的总时长
|
|
|
+ var oneSelectImageDuration: Float = 0.0
|
|
|
+ if selectedDataCount == selectedImageDataCount {
|
|
|
+ oneSelectImageDuration = (finallyStuckPoints.last ?? 0) - (finallyStuckPoints.first ?? 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3)素材全是图片处理
|
|
|
+ if selectedDataCount == selectedImageDataCount {
|
|
|
+ // lastCyclesSelectIndex != -1 已经设置过循环次数 应该是手动设置的值
|
|
|
+ if lastCyclesSelectIndex != -1 {
|
|
|
+ // 纯图片时 已经默认添加一次循环 所以要用lastCyclesSelectIndex - 1
|
|
|
+ if lastCyclesSelectIndex != 0 {
|
|
|
+ clipPoint(clipNum: selectedImageDataCount * lastCyclesSelectIndex - 1, oldPoints: stuckPointsTemp)
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ if oneSelectImageDuration < 10 {
|
|
|
+ lastCyclesSelectIndex = 0
|
|
|
+ while oneSelectImageDuration < 10 {
|
|
|
+ // 不够10S 时 一次加图片数量的卡点数
|
|
|
+ clipPoint(clipNum: selectedImageDataCount - 1, oldPoints: stuckPointsTemp)
|
|
|
+ oneSelectImageDuration = Float((finallyStuckPoints.last ?? 0) - (finallyStuckPoints.first ?? 0))
|
|
|
+
|
|
|
+ lastCyclesSelectIndex = lastCyclesSelectIndex + 1
|
|
|
+ }
|
|
|
+ speedSettingView.setSelectItem(index: lastCyclesSelectIndex, isSettingPlayer: false, enableInsert: true)
|
|
|
+ } else {
|
|
|
+ lastCyclesSelectIndex = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if finallyStuckPoints.count < 2 {
|
|
|
+ cShowHUB(superView: nil, msg: "视频资源导入失败,请重新选择!!")
|
|
|
+ exportResourceFailed()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置速度选择的位置
|
|
|
+ if speedSettingView.viewType == 1 {
|
|
|
+ speedSettingView.setSelectItem(index: lastSpeedSelectIndex, isSettingPlayer: false)
|
|
|
+ } else if speedSettingView.viewType == 2 {
|
|
|
+ speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex, isSettingPlayer: false)
|
|
|
+ } else if speedSettingView.viewType == 3 {
|
|
|
+ if lastCyclesSelectIndex != -1 {
|
|
|
+ speedSettingView.setSelectItem(index: lastCyclesSelectIndex, isSettingPlayer: false)
|
|
|
+ } else {
|
|
|
+ speedSettingView.setSelectItem(index: 0, isSettingPlayer: false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ updateTimeInfomation()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// MARK: - 同步/下载素材相关
|
|
|
+
|
|
|
+/// 同步/下载素材相关
|
|
|
+extension PQStuckPointEditerController {
|
|
|
+ /// 同步音乐相关数据
|
|
|
+ /// - Returns: <#description#>
|
|
|
+ func synchroMusicInfoData(resetSelectIndex: Bool = true) {
|
|
|
+ if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
|
|
|
+ synchroMarskView.show()
|
|
|
+ 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?.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(resetSelectIndex: resetSelectIndex)
|
|
|
+ }
|
|
|
+ self?.synchroMarskView.removeMarskView()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ self?.isSynchroMusicInfoSuccess = true
|
|
|
+ // 处理所有数据完成
|
|
|
+ self?.dealWithDataSuccess(resetSelectIndex: resetSelectIndex)
|
|
|
+ }
|
|
|
+ // 添加子视图
|
|
|
+ self?.addSubViews()
|
|
|
+ } else {
|
|
|
+ self?.synchroMarskView.removeMarskView()
|
|
|
+ cShowHUB(superView: nil, msg: "音乐信息加载失败,请重新选择音乐")
|
|
|
+ self?.navigationController?.popViewController(animated: true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if stuckPointMusicData?.localPath == nil || (stuckPointMusicData?.localPath?.count ?? 0) > 0 {
|
|
|
+ synchroMarskView.show()
|
|
|
+ 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(resetSelectIndex: resetSelectIndex)
|
|
|
+ } else {
|
|
|
+ self?.synchroMarskView.removeMarskView()
|
|
|
+ cShowHUB(superView: nil, msg: "音乐信息加载失败,请重新选择音乐")
|
|
|
+// BFUploadRemindView.showUploadRemindView(title: nil, attributedTitle: NSAttributedString(string: "加载音乐失败,请重新选择音乐"), summary: "", confirmTitle: nil) { [weak self] _, _ in
|
|
|
+ self?.navigationController?.popViewController(animated: true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ isStuckPointDataSuccess = true
|
|
|
+ // 处理所有数据完成
|
|
|
+ dealWithDataSuccess(resetSelectIndex: resetSelectIndex)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 导出相册数据
|
|
|
+ /// - Returns: <#description#>
|
|
|
+ func exportPhotoData() {
|
|
|
+ // 取消所有的导出
|
|
|
+ PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
|
|
|
+ exportSession.cancelExport()
|
|
|
+ }
|
|
|
+ var isHaveVideo: Bool = false
|
|
|
+ var failedExprot: Bool = false
|
|
|
+ if selectedMetarialData != nil, (selectedMetarialData?.count ?? 0) > 0 {
|
|
|
+ if synchroMarskView.superview == nil {
|
|
|
+ UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
|
|
|
+ }
|
|
|
+ let dispatchGroup = DispatchGroup()
|
|
|
+
|
|
|
+ for photo in selectedMetarialData! {
|
|
|
+ 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.selectedMetarialData?.first(where: { material in
|
|
|
+ material.asset == photo.asset
|
|
|
+ })
|
|
|
+// MARK: SanW-2021.11.15-不在导出到沙盒,直接用本地地址
|
|
|
+ tempPhoto?.locationPath = fileName.replacingOccurrences(of: "file://", with: "")
|
|
|
+ dispatchGroup.leave()
|
|
|
+// 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)")
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let curr = Date()
|
|
|
+// let assetResources = PHAssetResource.assetResources(for: photo.asset!)
|
|
|
+// if let rsc = assetResources.first(where: { res in
|
|
|
+// res.type == .video || res.type == .pairedVideo
|
|
|
+// }) {
|
|
|
+// PHAssetResourceManager.default().writeData(for: rsc, toFile: URL(fileURLWithPath: outFilePath), options: nil) { error in
|
|
|
+// if error == nil {
|
|
|
+// BFLog(message: "导出视频相exportAsynchronously \(String(describing: outFilePath)) \(Date().timeIntervalSince(curr))")
|
|
|
+// tempPhoto?.locationPath = outFilePath.replacingOccurrences(of: documensDirectory, with: "")
|
|
|
+// }else{
|
|
|
+// failedExprot = true
|
|
|
+// BFLog(message: "导出视频相exportAsynchro faile")
|
|
|
+// }
|
|
|
+// dispatchGroup.leave()
|
|
|
+// }
|
|
|
+//
|
|
|
+// }else {
|
|
|
+// BFLog(message: "导出视频相exportAsynchro faile")
|
|
|
+// dispatchGroup.leave()
|
|
|
+// }
|
|
|
+// }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dispatchGroup.notify(queue: DispatchQueue.main) { [weak self] in
|
|
|
+ if failedExprot {
|
|
|
+ cShowHUB(superView: nil, msg: "视频导入失败,请返回重试")
|
|
|
+ self?.exportResourceFailed()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ self?.isExportVideosSuccess = true
|
|
|
+ BFLog(message: "所有相册视频导出成功")
|
|
|
+ // 处理所有数据完成
|
|
|
+ if isHaveVideo {
|
|
|
+ self?.dealWithDataSuccess()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只有图片
|
|
|
+ if !isHaveVideo {
|
|
|
+ isExportVideosSuccess = true
|
|
|
+ BFLog(message: "所有相册视频导出成功")
|
|
|
+
|
|
|
+ dealWithDataSuccess()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func exportResourceFailed() {
|
|
|
+ DispatchQueue.main.async {
|
|
|
+ self.synchroMarskView.removeMarskView()
|
|
|
+ self.navigationController?.popViewController(animated: true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 处理所有数据完成
|
|
|
+ /// - Returns: <#description#>
|
|
|
+ /// resetSelectIndex : 是否重置用户选择的速度设置,在编辑界面切换音乐时不重置
|
|
|
+ func dealWithDataSuccess(resetSelectIndex: Bool = true) {
|
|
|
+ BFLog(message: "三组参数:\(isSynchroMusicInfoSuccess) \(isStuckPointDataSuccess) \(isExportVideosSuccess)")
|
|
|
+ if !isSynchroMusicInfoSuccess || !isStuckPointDataSuccess || !isExportVideosSuccess {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ createPorjectData()
|
|
|
+ BFLog(1, message: "界面编辑界面时参数 选择素材时长:\(selectedTotalDuration) 选择素材总数:\(selectedDataCount) 选择图片总数\(selectedImageDataCount) 再创建类型:\(String(describing: reCreateVideoData?.rhythmMode))")
|
|
|
+
|
|
|
+ if resetSelectIndex {
|
|
|
+ // 1 生成默认参数值
|
|
|
+ /*
|
|
|
+ - 当素材总时长∈[0-6)s 时,提示推荐仅配乐模式 or 快慢速模式
|
|
|
+ - 当素材总时长∈[6-80)s 时,卡点抛留倍数为1x
|
|
|
+ - 当素材总时长∈[80,120)s 时,卡点抛留倍数为2x
|
|
|
+ - 当素材总时长∈[120,160)s 时,卡点抛留倍数为3x
|
|
|
+ - 当素材总时长∈[160,200)s 时,卡点抛留倍数为4x
|
|
|
+ - 当素材总时长∈[200,240)s 时,卡点抛留倍数为5x
|
|
|
+ - 当素材总时长∈[240,∞)s 时,卡点抛留倍数为5x
|
|
|
+ */
|
|
|
+ // 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) || selectedTotalDuration >= 240 {
|
|
|
+ lastJumpSpeedSelectIndex = 4
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 默认进入快慢速模式
|
|
|
+ - 当素材总时长∈[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 || selectedTotalDuration > 144 {
|
|
|
+ lastSpeedSelectIndex = 5
|
|
|
+ } else if selectedTotalDuration >= 70, selectedTotalDuration < 120 {
|
|
|
+ lastSpeedSelectIndex = 4
|
|
|
+ } else if selectedTotalDuration >= 56, selectedTotalDuration < 70 {
|
|
|
+ lastSpeedSelectIndex = 3
|
|
|
+ } else if selectedTotalDuration >= 17.5, selectedTotalDuration < 56 {
|
|
|
+ lastSpeedSelectIndex = 2
|
|
|
+ } else if selectedTotalDuration >= 10.5, selectedTotalDuration < 17.5 {
|
|
|
+ lastSpeedSelectIndex = 1
|
|
|
+ } else if selectedTotalDuration > 0, selectedTotalDuration < 10.5 {
|
|
|
+ lastSpeedSelectIndex = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是再创作进来的按原视频的模式
|
|
|
+ if reCreateVideoData != nil {
|
|
|
+ BFLog(message: "是再创作进来的 \(reCreateVideoData!.rhythmMode)")
|
|
|
+ switch reCreateVideoData!.rhythmMode {
|
|
|
+ case 1:
|
|
|
+ editModelClick(sender: jumpPointBtn, reportLog: false)
|
|
|
+ case 2:
|
|
|
+ editModelClick(sender: speedStuckBtn, reportLog: false)
|
|
|
+
|
|
|
+ case 3:
|
|
|
+ editModelClick(sender: onlyMusicBtn, reportLog: false)
|
|
|
+ default: break
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 跳跃卡点不可用
|
|
|
+ if selectedTotalDuration < 6 && selectedDataCount != selectedImageDataCount {
|
|
|
+
|
|
|
+ let styleColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
|
|
|
+ jumpPointBtn.setTitleColor(UIColor.init(red: styleColor.rgbaf[0], green: styleColor.rgbaf[1], blue: styleColor.rgbaf[2], alpha: 0.3), for: .selected)
|
|
|
+
|
|
|
+ let pointEditNamalBackgroundColor = BFConfig.shared.pointEditNamalBackgroundColor
|
|
|
+ jumpPointBtn.backgroundColor = UIColor.init(red: pointEditNamalBackgroundColor.rgbaf[0], green: pointEditNamalBackgroundColor.rgbaf[1], blue: pointEditNamalBackgroundColor.rgbaf[2], alpha: 0.3)
|
|
|
+
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ 文档规则 https://w42nne6hzg.feishu.cn/docs/doccnQZm1uCfkU4QtJb5fLxYk4d#
|
|
|
+ */
|
|
|
+ // 2,根据所选择所有素材时长进入默认模式
|
|
|
+ // 全是图片
|
|
|
+ if selectedDataCount == selectedImageDataCount {
|
|
|
+ BFLog(message: "全是图片 \(selectedDataCount) \(selectedImageDataCount)")
|
|
|
+
|
|
|
+ // 默认进入跳跃卡点模式
|
|
|
+ editModelClick(sender: jumpPointBtn, reportLog: false)
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // 默认进入快慢速模式
|
|
|
+ if selectedTotalDuration > 0, selectedTotalDuration <= 144 {
|
|
|
+ editModelClick(sender: speedStuckBtn, reportLog: false)
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // 默认进入跳跃卡点模式
|
|
|
+ editModelClick(sender: jumpPointBtn, reportLog: false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ editModelClick(sender: lastEditModelBtn ?? jumpPointBtn, reportLog: false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|