|
@@ -10,6 +10,7 @@
|
|
|
enum createStickersModel {
|
|
|
case createStickersModelPoint // 卡点
|
|
|
case createStickersModelSpeed // 快慢速
|
|
|
+ case createStickersModelOnlyMusic //仅配乐
|
|
|
}
|
|
|
|
|
|
import Foundation
|
|
@@ -26,7 +27,7 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
var isSynchroMusicInfoSuccess: Bool = false
|
|
|
/// 当前所有的filter
|
|
|
var filters: Array = Array<ImageProcessingOperation>.init()
|
|
|
- // 选中的总时长
|
|
|
+ // 选中所有素材的的总时长 其中图片时长为1.5
|
|
|
var selectedTotalDuration: Float64 = 0
|
|
|
// 选择的总数
|
|
|
var selectedDataCount: Int = 0
|
|
@@ -48,6 +49,23 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
var isReCreate: Bool = false
|
|
|
// 最后一个选择的模式 BTN 用于还原选中状态
|
|
|
var lastEditModelBtn: UIButton?
|
|
|
+
|
|
|
+ // add by ak 最大、最小速度 有固定值和自定义,当快慢速下两个值都有效,当跳跃卡点只有maxSpeed有效
|
|
|
+ var maxSpeed: Float = 0.0
|
|
|
+ var minSpeed: Float = 0.0
|
|
|
+
|
|
|
+ //快慢速最后一次选择的速度位置
|
|
|
+ var lastSpeedSelectIndex:Int = 0
|
|
|
+ //跳跃卡点最后一次选择的速度位置
|
|
|
+ var lastJumpSpeedSelectIndex:Int = 0
|
|
|
+
|
|
|
+ //当前选择的玩法模式
|
|
|
+ var currentCreateStickersModel:createStickersModel = .createStickersModelSpeed
|
|
|
+
|
|
|
+ //最终使用的卡点数据
|
|
|
+ var finallyStuckPoints: Array = Array<Float>.init()
|
|
|
+ //最终使用的音频时长
|
|
|
+ var finallyUserAudioTime:Float = 0.0
|
|
|
// 下一步
|
|
|
lazy var nextBtn: UIButton = {
|
|
|
let nextBtn = UIButton(type: .custom)
|
|
@@ -235,10 +253,9 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
lazy var speedStuckBtn: UIButton = {
|
|
|
let speedStuckBtn = UIButton(type: .custom)
|
|
|
speedStuckBtn.addTarget(self, action: #selector(editModelClick(sender:)), for: .touchUpInside)
|
|
|
- speedStuckBtn.isSelected = true
|
|
|
speedStuckBtn.setBackgroundImage(UIImage().BF_Image(named: "speedstuck_n"), for: .normal)
|
|
|
speedStuckBtn.setBackgroundImage(UIImage().BF_Image(named: "speedstuck_h"), for: .selected)
|
|
|
- lastEditModelBtn = speedStuckBtn
|
|
|
+
|
|
|
speedStuckBtn.tag = 1
|
|
|
speedStuckBtn.adjustsImageWhenHighlighted = false
|
|
|
return speedStuckBtn
|
|
@@ -298,8 +315,16 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
let speedSettingView = PQSpeedSettingView()
|
|
|
speedSettingView.backgroundColor = .clear
|
|
|
speedSettingView.viewType = 1
|
|
|
- speedSettingView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed in
|
|
|
- BFLog(message: "maxSpeed is\(maxSpeed) minSpeed \(minSpeed)")
|
|
|
+ speedSettingView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed,selectIndex in
|
|
|
+ BFLog(message: "固定maxSpeed is\(maxSpeed) minSpeed \(minSpeed)")
|
|
|
+ //更新最后一次选择的位置恢复时使用
|
|
|
+ if(speedSettingView.viewType == 2){
|
|
|
+ self?.lastJumpSpeedSelectIndex = selectIndex
|
|
|
+ }else{
|
|
|
+ self?.lastSpeedSelectIndex = selectIndex
|
|
|
+ }
|
|
|
+ self?.maxSpeed = maxSpeed
|
|
|
+ self?.minSpeed = minSpeed
|
|
|
if maxSpeed == 0 && minSpeed == 0 {
|
|
|
self?.customSpeedSettingView.isHidden = false
|
|
|
self?.customSpeedSettingView.isJumpSpeedModel = speedSettingView.viewType == 2
|
|
@@ -314,6 +339,12 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
let customSpeedSettingView = PQCustomSpeedSettingView(frame: CGRect(x: 0, y: cScreenHeigth - 354, width: cScreenWidth, height: 354))
|
|
|
customSpeedSettingView.isHidden = true
|
|
|
customSpeedSettingView.isJumpSpeedModel = false
|
|
|
+ customSpeedSettingView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed ,isJumpSpeedModel in
|
|
|
+ self?.maxSpeed = maxSpeed
|
|
|
+ self?.minSpeed = minSpeed
|
|
|
+ BFLog(message: "自定义速度maxSpeed is\(maxSpeed) minSpeed \(minSpeed) \(isJumpSpeedModel)")
|
|
|
+
|
|
|
+ }
|
|
|
return customSpeedSettingView
|
|
|
|
|
|
}()
|
|
@@ -470,18 +501,24 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
if sender.tag == 1 { // 快慢速
|
|
|
speedStuckBtnGifName = "speedstuck_h"
|
|
|
jumpPointBtnGifName = "jumpPoint_n"
|
|
|
+ currentCreateStickersModel = .createStickersModelSpeed
|
|
|
+ speedSettingView.setSelectItem(index: lastSpeedSelectIndex)
|
|
|
|
|
|
|
|
|
} else if sender.tag == 2 { // 跳跃卡点
|
|
|
speedStuckBtnGifName = "speedstuck_n"
|
|
|
jumpPointBtnGifName = "jumpPoint_h"
|
|
|
-
|
|
|
+ speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex)
|
|
|
+ currentCreateStickersModel = .createStickersModelPoint
|
|
|
+
|
|
|
} else if sender.tag == 3 { // 仅音乐
|
|
|
speedStuckBtnGifName = "speedstuck_n"
|
|
|
jumpPointBtnGifName = "jumpPoint_n"
|
|
|
-
|
|
|
+ currentCreateStickersModel = .createStickersModelOnlyMusic
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+ settingPlayerView()
|
|
|
speedStuckBtnGif.kf.setImage(with: URL(fileURLWithPath: Bundle.init().BF_mainbundle().path(forResource:speedStuckBtnGifName , ofType: ".gif")!))
|
|
|
jumpPointBtnGif.kf.setImage(with: URL(fileURLWithPath: Bundle.init().BF_mainbundle().path(forResource: jumpPointBtnGifName , ofType: ".gif")!))
|
|
|
}
|
|
@@ -739,7 +776,7 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
videoSize = CGSize(width: minSlider, height: maxSlider)
|
|
|
}
|
|
|
|
|
|
- let maxValue = max(videoSize.width, videoSize.height ?? 0)
|
|
|
+ let maxValue = max(videoSize.width, videoSize.height )
|
|
|
if maxValue > 1920 {
|
|
|
let maxRation = 1920 / maxValue
|
|
|
|
|
@@ -760,7 +797,7 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
|
|
|
// 2,创建滤镜
|
|
|
let beginTime: TimeInterval = Date().timeIntervalSince1970
|
|
|
- mStickers = createStickers(sections: projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(projectModel.sData?.videoMetaData?.videoHeight ?? 0)), model: .createStickersModelPoint)
|
|
|
+ mStickers = createStickers(sections: projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(projectModel.sData?.videoMetaData?.videoHeight ?? 0)), model: currentCreateStickersModel)
|
|
|
playerView.mStickers = mStickers
|
|
|
|
|
|
let end: TimeInterval = Date().timeIntervalSince1970
|
|
@@ -768,12 +805,20 @@ class PQStuckPointEditerController: PQBaseViewController {
|
|
|
|
|
|
// 3,设置音频
|
|
|
let audioPath = stuckPointMusicData?.localPath ?? ""
|
|
|
+
|
|
|
BFLog(message: "初始化音频播放器的音频地址为:\(audioPath)")
|
|
|
playerView.stop()
|
|
|
// 这里的测试这个音乐播放有问题
|
|
|
// self.playerView.updateAsset(URL(fileURLWithPath: "63930549652d74e477141e3b79c8d29a9ef8af81625053214516.mp3", relativeTo:Bundle.main.resourceURL!), videoComposition: nil, audioMixModel: nil)
|
|
|
- playerView.updateAsset(URL(fileURLWithPath: documensDirectory + audioPath), videoComposition: nil, audioMixModel: nil)
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+ //设置音乐的拼接范围,开始:推荐的卡点 结束:点到的倒数第二位
|
|
|
+ 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))
|
|
|
+
|
|
|
+ playerView.updateAsset(URL(fileURLWithPath: documensDirectory + audioPath), videoComposition: nil, audioMixModel: nil,originMusicDuration:finallyUserAudioTime,clipAudioRange: clipAudioRange)
|
|
|
+
|
|
|
let end2: TimeInterval = Date().timeIntervalSince1970
|
|
|
BFLog(message: "updateAsset tiskskskskme \(end2 - end)")
|
|
|
// 4, 设置播放器的输出画布大小
|
|
@@ -891,9 +936,14 @@ extension PQStuckPointEditerController {
|
|
|
/// - inputSize: 画布大小
|
|
|
/// - Returns: filters 数据 播放器可直接使用
|
|
|
func createStickers(sections: List<PQEditSectionModel>, inputSize _: CGSize = .zero, model: createStickersModel = .createStickersModelPoint) -> [PQEditVisionTrackMaterialsModel] {
|
|
|
+
|
|
|
+ // 推荐卡点数
|
|
|
+ dealParameter(model: model)
|
|
|
+ let stuckPoints: Array = finallyStuckPoints
|
|
|
+
|
|
|
// 保存滤镜对象数据
|
|
|
var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
|
|
|
- if model == .createStickersModelPoint {
|
|
|
+ if model == .createStickersModelPoint { //跳跃卡点
|
|
|
for section in sections {
|
|
|
if section.sectionType == "normal" {
|
|
|
// 推荐卡点数
|
|
@@ -907,13 +957,12 @@ extension PQStuckPointEditerController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* 快慢速模式下取卡点
|
|
|
+ /* 快慢速模式下取卡点 1 3 5
|
|
|
- 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
|
|
|
- 适中为每两个点位取一个,即0,3,6,9 12
|
|
|
- 慢节奏为每三个点位取一个,即0 5 10 15
|
|
|
慢节奏要做特殊处理
|
|
|
- 5d or L/1.23
|
|
|
- (*当输入素材为L ∈(0-10.5]s 时,判断与5d之间的关系,若L/1.2≥5d,则取5d;若L/1.2<5d,则取L/1.2)
|
|
|
+
|
|
|
*/
|
|
|
// 跳跃卡点模式下根据不同速度 取卡点 1,2,3
|
|
|
/*
|
|
@@ -1001,7 +1050,7 @@ extension PQStuckPointEditerController {
|
|
|
BFLog(message: "bbbbbindexindeindexxindexindexindex \(index) \(point)")
|
|
|
let sticker: PQEditVisionTrackMaterialsModel = clipFilters[index]
|
|
|
sticker.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
|
|
|
- // TODO: 不是最好方案
|
|
|
+
|
|
|
sticker.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
|
|
|
// 卡点的时间 > in out 值 这里就会出现鬼畜效果
|
|
|
let timelineInterval = sticker.timelineOut - sticker.timelineIn
|
|
@@ -1034,136 +1083,312 @@ extension PQStuckPointEditerController {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
+ } else if model == .createStickersModelSpeed{//快慢速
|
|
|
for section in sections {
|
|
|
if section.sectionType == "normal" {
|
|
|
- // 推荐卡点数
|
|
|
- var stuckPoints: Array = Array<Float>.init()
|
|
|
-
|
|
|
- var stuckPointsTemp = Array<Float>.init()
|
|
|
- for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
|
|
|
- BFLog(message: "原所有卡点数:\(index) \(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))")
|
|
|
- if Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) > CMTimeGetSeconds(playeTimeRange.start), Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) < CMTimeGetSeconds(playeTimeRange.end) {
|
|
|
- stuckPointsTemp.append(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 根据不同速度 取卡点 1,2,3
|
|
|
- /*
|
|
|
- - 快节奏为选中区域的所有点位,即0,1,2,3,4……
|
|
|
- - 适中为每两个点位取一个,即0,2,4,6……
|
|
|
- - 慢节奏为每三个点位取一个,即0,3,6,9……
|
|
|
- */
|
|
|
- BFLog(message: "stuckPointMusicData?.speed is \(String(describing: stuckPointMusicData?.speed))")
|
|
|
- for (index, point) in stuckPointsTemp.enumerated() {
|
|
|
- if stuckPointMusicData?.speed == 1 {
|
|
|
- stuckPoints.append(Float(point))
|
|
|
- } else if stuckPointMusicData?.speed == 2 {
|
|
|
- if index % 2 == 0 {
|
|
|
- stuckPoints.append(point)
|
|
|
+
|
|
|
+ BFLog(message: "stuckPoints count is \(stuckPoints.count)")
|
|
|
+
|
|
|
+ //一共裁剪的段数
|
|
|
+ var totalClipNum:Int = 0
|
|
|
+ for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
|
|
|
+ if sticker.type == StickerType.VIDEO.rawValue {
|
|
|
+ let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
|
|
|
+
|
|
|
+ var i:Int = 0
|
|
|
+ //一个视频切的的段落
|
|
|
+ var oneVideoClipNum:Int = 0
|
|
|
+
|
|
|
+ var realUsedMusicDuration = 0.0
|
|
|
+ while realUsedMusicDuration <= CMTimeGetSeconds(asset.duration) {
|
|
|
+ //快速段
|
|
|
+ let LA = maxSpeed * (stuckPoints[i+1] - stuckPoints[i])
|
|
|
+ realUsedMusicDuration = realUsedMusicDuration + Float64(LA)
|
|
|
+ if(realUsedMusicDuration > CMTimeGetSeconds(asset.duration)){ break}
|
|
|
+ oneVideoClipNum = oneVideoClipNum + 1
|
|
|
+ //慢速段
|
|
|
+ let LB = minSpeed * (stuckPoints[i+2] - stuckPoints[i+1])
|
|
|
+ realUsedMusicDuration = realUsedMusicDuration + Float64(LB)
|
|
|
+ oneVideoClipNum = oneVideoClipNum + 1
|
|
|
+ i = i + 1
|
|
|
}
|
|
|
+ BFLog(message: "单个视频\(sticker.locationPath)时长::\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(oneVideoClipNum)")
|
|
|
+
|
|
|
+ for clipindex in 0 ... oneVideoClipNum - 1 {
|
|
|
+ // deep copy sticker model 防止只有一个对象
|
|
|
+ let stickerjson = sticker.toJSONString(prettyPrint: false)
|
|
|
+ let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
|
|
|
+ // 设置循环模式和适配模式
|
|
|
+ deepCopySticker?.generateDefaultValues()
|
|
|
+ //当前分段的速度
|
|
|
+ let tempSpeed = (totalClipNum + clipindex) % 2 == 0 ? maxSpeed : minSpeed
|
|
|
+ if(totalClipNum + 1 + clipindex < stuckPoints.count){
|
|
|
+ deepCopySticker?.speedRate = tempSpeed
|
|
|
+ deepCopySticker?.model_in = Float64(tempSpeed) * Float64(stuckPoints[totalClipNum + clipindex] - (stuckPoints.first ?? 0.0))
|
|
|
+ deepCopySticker?.out = Float64(tempSpeed) * Float64(stuckPoints[totalClipNum + 1 + clipindex] - (stuckPoints.first ?? 0.0))
|
|
|
+ deepCopySticker?.timelineIn = Float64(stuckPoints[totalClipNum + clipindex])
|
|
|
+ deepCopySticker?.timelineOut = Float64(stuckPoints[totalClipNum + 1 + clipindex])
|
|
|
+ }
|
|
|
+
|
|
|
+ BFLog(message: " crilp is in \(deepCopySticker?.model_in ?? 0) out \(deepCopySticker?.out ?? 0) timelineIN:\(deepCopySticker?.timelineIn ?? 0) timelineOUT:\(deepCopySticker?.timelineOut ?? 0) 总时长\(CMTimeGetSeconds(asset.duration)) speedRate:\(String(describing: deepCopySticker?.speedRate))")
|
|
|
|
|
|
- } else if stuckPointMusicData?.speed == 3 {
|
|
|
- if index % 3 == 0 {
|
|
|
- stuckPoints.append(point)
|
|
|
+ if deepCopySticker != nil {
|
|
|
+ stickers.append(deepCopySticker!)
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
+ totalClipNum = totalClipNum + oneVideoClipNum - 1
|
|
|
+ } else if sticker.type == StickerType.IMAGE.rawValue {
|
|
|
+ sticker.generateDefaultValues()
|
|
|
+ sticker.timelineIn = Float64(stuckPoints[totalClipNum])
|
|
|
+ sticker.timelineOut = Float64(stuckPoints[totalClipNum + 1])
|
|
|
+ stickers.append(sticker)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- for point in stuckPoints {
|
|
|
- BFLog(message: "没有 start end 计算后的卡点数\(point)")
|
|
|
- }
|
|
|
- if stuckPoints.first != nil {
|
|
|
- stuckPoints.removeFirst()
|
|
|
- }
|
|
|
- if stuckPoints.last != nil {
|
|
|
- stuckPoints.removeLast()
|
|
|
- }
|
|
|
- // 开始时间是服务器返回, 结果时间根据策略计算的
|
|
|
- stuckPoints.insert(Float(CMTimeGetSeconds(playeTimeRange.start)), at: 0)
|
|
|
- stuckPoints.insert((Float(CMTimeGetSeconds(playeTimeRange.end))), at: stuckPoints.count)
|
|
|
-
|
|
|
- for point in stuckPoints {
|
|
|
- BFLog(message: "有 start end 计算后的卡点数\(point)")
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ } else if model == .createStickersModelOnlyMusic{ //仅音乐 首尾相连播放
|
|
|
+ for section in sections {
|
|
|
+ if section.sectionType == "normal" {
|
|
|
|
|
|
BFLog(message: "stuckPoints count is \(stuckPoints.count)")
|
|
|
- // 当用户上传视觉素材个数大于等于音乐选择区域节拍分割个数时,无需进行视频分割,只显示卡点数-1 个素材
|
|
|
- if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count >= stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
|
|
|
- for (index, sticker) in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().enumerated() {
|
|
|
- if index == stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
|
|
|
- BFLog(message: "到达卡点数量")
|
|
|
- break
|
|
|
- }
|
|
|
- BFLog(message: "创建 filter start :\(sticker.timelineIn) end :\(sticker.timelineOut) type is \(sticker.type) \(sticker.locationPath)")
|
|
|
- sticker.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
|
|
|
- sticker.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
|
|
|
- BFLog(message: "卡点 间隔 \(sticker.timelineIn - sticker.timelineOut)")
|
|
|
+ //已经计算过所有贴纸的累计时长
|
|
|
+ var totalStickerTimer:Float64 = 0
|
|
|
+ for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
|
|
|
+ if sticker.type == StickerType.VIDEO.rawValue {
|
|
|
+ let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
|
|
|
+
|
|
|
+ BFLog(message: "单个视频\(sticker.locationPath)时长::\(CMTimeGetSeconds(asset.duration))")
|
|
|
sticker.generateDefaultValues()
|
|
|
- stickers.append(sticker)
|
|
|
- }
|
|
|
-
|
|
|
- } else {
|
|
|
- // 卡点数 > 选择素材数
|
|
|
-
|
|
|
- // 第一种情况:全是图片,图片回环播放
|
|
|
- if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
|
|
|
- for (index, point) in stuckPoints.enumerated() {
|
|
|
- let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
|
|
|
- BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
|
|
|
- let stickerjson = sticker.toJSONString(prettyPrint: false)
|
|
|
+ sticker.model_in = 0
|
|
|
+ sticker.out = CMTimeGetSeconds(asset.duration)
|
|
|
+
|
|
|
+ sticker.timelineIn = totalStickerTimer
|
|
|
+ sticker.timelineOut = sticker.timelineIn + sticker.out
|
|
|
+
|
|
|
+ totalStickerTimer = totalStickerTimer + sticker.out
|
|
|
+
|
|
|
+
|
|
|
+ } else if sticker.type == StickerType.IMAGE.rawValue {
|
|
|
+ sticker.generateDefaultValues()
|
|
|
+ sticker.timelineIn = totalStickerTimer
|
|
|
+ sticker.timelineOut = sticker.timelineIn + sticker.out
|
|
|
+
|
|
|
+ totalStickerTimer = totalStickerTimer + sticker.out
|
|
|
|
|
|
- let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
|
|
|
- if deepCopySticker!.type == StickerType.IMAGE.rawValue {
|
|
|
- if index + 1 < stuckPoints.count {
|
|
|
- deepCopySticker!.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
|
|
|
- deepCopySticker!.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
|
|
|
- if deepCopySticker != nil {
|
|
|
- deepCopySticker?.generateDefaultValues()
|
|
|
- stickers.append(deepCopySticker!)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 第二种情况:有视频要进行分割
|
|
|
- let clipFilters = clipVideoMerage(section: section, stuckPoints: stuckPoints)
|
|
|
- for (index, point) in stuckPoints.enumerated() {
|
|
|
- BFLog(message: "aaaaaindexindeindexxindexindexindex \(index) \(point)")
|
|
|
- if index + 1 < stuckPoints.count, index < clipFilters.count {
|
|
|
- BFLog(message: "bbbbbindexindeindexxindexindexindex \(index) \(point)")
|
|
|
- let sticker: PQEditVisionTrackMaterialsModel = clipFilters[index]
|
|
|
- let spit: Int = 2
|
|
|
- sticker.timelineIn = 57.5 + Double(index * spit)
|
|
|
- sticker.timelineOut = sticker.timelineIn + Double(spit)
|
|
|
- sticker.speedRate = index % 2 == 0 ? 0.1 : 2
|
|
|
-
|
|
|
- sticker.model_in = Double(index * spit)
|
|
|
- sticker.out = sticker.model_in + Double(spit)
|
|
|
-
|
|
|
-// let spit: Int = 2
|
|
|
-// sticker.timelineIn = 57.5
|
|
|
-// sticker.timelineOut = sticker.timelineIn + 24
|
|
|
-// sticker.speedRate = index % 2 == 0 ? 0.1 : 2
|
|
|
- //// sticker.speedRate = 0.1
|
|
|
-// sticker.model_in = 0
|
|
|
-// sticker.out = sticker.model_in + 24
|
|
|
-
|
|
|
- if stickers.count < 12 {
|
|
|
- BFLog(message: "index is \(index)分割后 创建 filter timelineIn :\(sticker.timelineIn) timelineOut :\(sticker.timelineOut) in :\(sticker.model_in) out:\(sticker.out) type is \(sticker.type) 显示总时长为:\(sticker.timelineOut - sticker.timelineIn) 裁剪总时长\(sticker.out - sticker.model_in)")
|
|
|
-
|
|
|
- stickers.append(sticker)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
+
|
|
|
+ stickers.append(sticker)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return stickers
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ /// 根据档位取最后使用的卡点数据
|
|
|
+ /// - Parameter seed: 档位速度
|
|
|
+ /// - Returns: 最后使用的卡点
|
|
|
+ func getUsedStuckPoint(seed:Int) -> Array<Float> {
|
|
|
+ // 推荐卡点数
|
|
|
+ var stuckPoints: Array = Array<Float>.init()
|
|
|
+
|
|
|
+ var stuckPointsTemp = Array<Float>.init()
|
|
|
+ for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
|
|
|
+ BFLog(message: "原所有卡点数:\(index) \(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))")
|
|
|
+ if Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) > (stuckPointMusicData?.startTime ?? 0) {
|
|
|
+ stuckPointsTemp.append(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ 一,快慢速模式下取卡点 1 3 5
|
|
|
+ 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
|
|
|
+ 适中为每两个点位取一个,即0,3,6,9 12
|
|
|
+ 慢节奏为每三个点位取一个,即0 5 10 15
|
|
|
+
|
|
|
+ 二,跳跃卡点模式下根据不同速度 取卡点 1,2,3
|
|
|
+ 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
|
|
|
+ 适中为每两个点位取一个,即0,2,4,6……
|
|
|
+ 慢节奏为每三个点位取一个,即0,3,6,9……
|
|
|
+ */
|
|
|
+ BFLog(message: "stuckPointMusicData?.speed is \(String(describing: stuckPointMusicData?.speed))")
|
|
|
+
|
|
|
+ //不丢
|
|
|
+ if(seed == 1){
|
|
|
+ stuckPoints = stuckPointsTemp
|
|
|
+ }else{
|
|
|
+ //根据档位要丢
|
|
|
+ for (index, point) in stuckPointsTemp.enumerated() {
|
|
|
+ if index % seed == 0 {
|
|
|
+ stuckPoints.append(point)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for point in stuckPoints {
|
|
|
+ BFLog(message: "没有 start end 计算后的卡点数\(point)")
|
|
|
+ }
|
|
|
+ if stuckPoints.first != nil {
|
|
|
+ stuckPoints.removeFirst()
|
|
|
+ }
|
|
|
+ if stuckPoints.last != nil {
|
|
|
+ stuckPoints.removeLast()
|
|
|
+ }
|
|
|
+ // 开始时间是服务器返回, 结果时间根据策略计算的
|
|
|
+ stuckPoints.insert(Float(stuckPointMusicData?.startTime ?? 0), at: 0)
|
|
|
+ stuckPoints.insert(Float(stuckPointMusicData?.endTime ?? 0), at: stuckPoints.count)
|
|
|
+
|
|
|
+ for point in stuckPoints {
|
|
|
+ BFLog(message: "有 start end 计算后的卡点数\(point)")
|
|
|
+ }
|
|
|
+
|
|
|
+ BFLog(message: "stuckPoints count is \(stuckPoints.count)")
|
|
|
+
|
|
|
+ return stuckPoints
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 根据不同模式model, maxSpeed ,minSpeed, self?.stuckPointMusicData?.speed 档位,生成音乐时长和最终使用的卡点信息
|
|
|
+ func dealParameter(model:createStickersModel){
|
|
|
+
|
|
|
+ //清空上一次使用的卡点数据
|
|
|
+ finallyStuckPoints.removeAll()
|
|
|
+ //注意推荐时间位置和后面最近的卡点时间与0.3的关系
|
|
|
+ //保存丢卡点处理后的卡点信息推荐开始到最后倒数第二个
|
|
|
+ let stuckPointsTemp = getUsedStuckPoint(seed: stuckPointMusicData?.speed ?? 0)
|
|
|
+
|
|
|
+ //最后真正要使用推荐结束时长
|
|
|
+ var realUsedMusicDuration:Float = 0.0
|
|
|
+ switch model {
|
|
|
+ case .createStickersModelPoint://跳跃卡点
|
|
|
+ // L/(n+1) L -原视觉素材总时长 n-抛留倍数
|
|
|
+ realUsedMusicDuration = Float(selectedTotalDuration) / (Float(maxSpeed) + 1)
|
|
|
+
|
|
|
+ //要拼接的段数
|
|
|
+ var clipNum:Int = 0
|
|
|
+ //所有段的时长总和
|
|
|
+ var tempTime:Float = 0.0
|
|
|
+ var i:Int = 0
|
|
|
+ while tempTime < Float(selectedTotalDuration) {
|
|
|
+
|
|
|
+ //回环从头取
|
|
|
+ if(i+2 > stuckPointsTemp.count){
|
|
|
+ i = 0
|
|
|
+ }
|
|
|
+ //快速段
|
|
|
+ let LA = (stuckPointsTemp[i+1] - stuckPointsTemp[i])
|
|
|
+ tempTime = tempTime + Float(LA)
|
|
|
+ if(tempTime > Float(selectedTotalDuration)){ break}
|
|
|
+ //慢速段
|
|
|
+ let LB = (stuckPointsTemp[i+2] - stuckPointsTemp[i+1])
|
|
|
+ tempTime = tempTime + Float(LB)
|
|
|
+ i = i + 1
|
|
|
+ clipNum = clipNum + 1
|
|
|
+ }
|
|
|
+
|
|
|
+ //拼接要使用的卡点信息
|
|
|
+ for i in 0...clipNum {
|
|
|
+ finallyStuckPoints.append(stuckPointsTemp[i % stuckPointsTemp.count] + Float(i / stuckPointsTemp.count) * (stuckPointsTemp.first ?? 0))
|
|
|
+ }
|
|
|
+
|
|
|
+ break
|
|
|
+ case .createStickersModelSpeed://快慢速
|
|
|
+
|
|
|
+ /*
|
|
|
+ - A-视频中的快速片段
|
|
|
+ - B-视频中的慢速片段
|
|
|
+ - d-在一档下音乐每个点位时长
|
|
|
+ - n-不同音乐档位对应的d倍数,快节奏时,n=1;适中时,n=3;慢节奏时,n=5
|
|
|
+ - L-原视觉素材时长
|
|
|
+ - x-视频在A片段的播放倍速
|
|
|
+ - y-视频在B片段的播放倍速
|
|
|
+ */
|
|
|
+ //LA=x*n*d,LB=y*n*d (n=1/3/5) 注:视频经过快慢速处理后的总时长约=L*2/(x+y)
|
|
|
+ BFLog(message: "Ax快速为:\(maxSpeed) By慢速为:\(minSpeed) 档位 N为:\(stuckPointMusicData?.speed ?? 0) 使用的卡点总数:\(stuckPointsTemp.count)")
|
|
|
+
|
|
|
+ //1,计算 AB 段的个数
|
|
|
+ var i:Int = 0
|
|
|
+ var LACount:Int = 0
|
|
|
+ var LBCount:Int = 0
|
|
|
+ while realUsedMusicDuration <= Float(selectedTotalDuration) {
|
|
|
+
|
|
|
+ //回环从头取
|
|
|
+ if(i+2 > stuckPointsTemp.count){
|
|
|
+ i = 0
|
|
|
+ }
|
|
|
+ //快速段
|
|
|
+ let LA = maxSpeed * (stuckPointsTemp[i+1] - stuckPointsTemp[i])
|
|
|
+ realUsedMusicDuration = realUsedMusicDuration + Float(LA)
|
|
|
+ if(realUsedMusicDuration > Float(selectedTotalDuration)){ break}
|
|
|
+ LACount = LACount + 1
|
|
|
+ BFLog(message: "快速段段段段段段后LACount:\(LACount) realUsedMusicDuration:\(realUsedMusicDuration)")
|
|
|
+ //慢速段
|
|
|
+ let LB = minSpeed * (stuckPointsTemp[i+2] - stuckPointsTemp[i+1])
|
|
|
+ realUsedMusicDuration = realUsedMusicDuration + Float(LB)
|
|
|
+ LBCount = LBCount + 1
|
|
|
+ BFLog(message: "慢速段段段段段段后LACount:\(LACount) realUsedMusicDuration:\(realUsedMusicDuration)")
|
|
|
+
|
|
|
+ i = i + 1
|
|
|
+ }
|
|
|
+
|
|
|
+ //2,拼接要使用的卡点信息
|
|
|
+ for (index,point) in stuckPointsTemp.enumerated() {
|
|
|
+
|
|
|
+ if(index < LACount + LBCount){
|
|
|
+ finallyStuckPoints.append(point)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //2.1不足所有卡点回环拼接
|
|
|
+ if( LACount + LBCount > stuckPointsTemp.count ){
|
|
|
+
|
|
|
+ for (index,point) in stuckPointsTemp.enumerated() {
|
|
|
+ if(index <= LACount + LBCount - stuckPointsTemp.count){
|
|
|
+ finallyStuckPoints.append(Float((stuckPointsTemp.last ?? 0) + point))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ BFLog(message: "计算后的\(realUsedMusicDuration) LACount:\(LACount) LBCount \(LBCount) 估算时长为:\(selectedTotalDuration * 2 / Float64(maxSpeed + minSpeed))")
|
|
|
+
|
|
|
+ break
|
|
|
+ case .createStickersModelOnlyMusic://仅音乐
|
|
|
+ //时长为所有素材总时长
|
|
|
+ finallyStuckPoints.append(Float(stuckPointMusicData?.startTime ?? 0) + Float(selectedTotalDuration))
|
|
|
+
|
|
|
+ break
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //计算最后使用的音频时长, 如果不用拼接音频时长度是卡点的倒数第二位时间
|
|
|
+ let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (stuckPointMusicData?.localPath ?? "")), options: nil)
|
|
|
+
|
|
|
+ //原推荐卡点的倒数第二位时间
|
|
|
+ let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2] )) / Float(BASE_FILTER_TIMESCALE)
|
|
|
+
|
|
|
+ finallyUserAudioTime = Float(lastSecondPoint)
|
|
|
+ if(finallyStuckPoints.last ?? 0 > Float(CMTimeGetSeconds(asset.duration))){
|
|
|
+
|
|
|
+ finallyUserAudioTime = Float(finallyStuckPoints.last ?? 0) + (Float(CMTimeGetSeconds(asset.duration)) - Float(lastSecondPoint))
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ playeTimeRange = CMTimeRange(start: CMTime(value: CMTimeValue( Float(stuckPointMusicData?.startTime ?? 0) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float(finallyStuckPoints.last ?? 0)) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
|
|
|
+
|
|
|
+ // 更新裁剪时间条的的ui数据
|
|
|
+ stuckPointCuttingView.videoDuration = CGFloat(finallyUserAudioTime)
|
|
|
+ stuckPointCuttingView.updateEndTime(startTime:CGFloat(CMTimeGetSeconds( playeTimeRange.start)), endTime: CGFloat( CMTimeGetSeconds( playeTimeRange.end)))
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// MARK: - 同步/下载素材相关
|
|
@@ -1187,7 +1412,7 @@ extension PQStuckPointEditerController {
|
|
|
self?.stuckPointMusicData?.speed = newMusicData?.speed ?? 2
|
|
|
}
|
|
|
if (self?.stuckPointMusicData?.rhythmSdata.count ?? 0) > 0 && (((self?.selectedDataCount ?? 0) - (self?.selectedImageDataCount ?? 0)) > 0 || (self?.selectedImageDataCount ?? 0) > 0 || (self?.selectedTotalDuration ?? 0) > 0) {
|
|
|
- self?.stuckPointMusicData?.endTime = (self?.stuckPointMusicData?.startTime ?? 0) + (self?.stuckPointMusicData?.stuckPointCuttingTime(videoCount: (self?.selectedDataCount ?? 0) - (self?.selectedImageDataCount ?? 0), imageCount: self?.selectedImageDataCount ?? 0, totalDuration: self?.selectedTotalDuration ?? 0) ?? 0)
|
|
|
+// self?.stuckPointMusicData?.endTime = (self?.stuckPointMusicData?.startTime ?? 0) + (self?.stuckPointMusicData?.stuckPointCuttingTime(videoCount: (self?.selectedDataCount ?? 0) - (self?.selectedImageDataCount ?? 0), imageCount: self?.selectedImageDataCount ?? 0, totalDuration: self?.selectedTotalDuration ?? 0) ?? 0)
|
|
|
}
|
|
|
self?.stuckPointCuttingView.updateEndTime(startTime: CGFloat(self?.stuckPointMusicData?.startTime ?? 0), endTime: CGFloat(self?.stuckPointMusicData?.endTime ?? 0))
|
|
|
if self?.stuckPointMusicData?.localPath == nil || (self?.stuckPointMusicData?.localPath?.count ?? 0) > 0 {
|
|
@@ -1337,8 +1562,93 @@ extension PQStuckPointEditerController {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- playeTimeRange = CMTimeRange(start: CMTimeMakeWithSeconds(stuckPointMusicData?.startTime ?? 0, preferredTimescale: BASE_FILTER_TIMESCALE), end: CMTimeMakeWithSeconds(stuckPointMusicData?.endTime ?? 0, preferredTimescale: BASE_FILTER_TIMESCALE))
|
|
|
createPorjectData()
|
|
|
- settingPlayerView()
|
|
|
+
|
|
|
+ BFLog(message: "界面编辑界面时参数 选择素材时长:\(selectedTotalDuration) 选择素材总数:\(selectedDataCount) 选择图片总数\(selectedImageDataCount)")
|
|
|
+
|
|
|
+
|
|
|
+ //1 生成默认参数值
|
|
|
+ /*
|
|
|
+ - 当素材总时长∈[0-6)s 时,提示推荐仅配乐模式 or 快慢速模式
|
|
|
+ - 当素材总时长∈[6-80)s 时,卡点抛留倍数为1x,即留1抛1
|
|
|
+ - 当素材总时长∈[80,120)s 时,卡点抛留倍数为2x,即留1抛2
|
|
|
+ - 当素材总时长∈[120,160)s 时,卡点抛留倍数为3x,即留1抛3
|
|
|
+ - 当素材总时长∈[160,200)s 时,卡点抛留倍数为4x,即留1抛4
|
|
|
+ - 当素材总时长∈[200,240)s 时,卡点抛留倍数为5x,即留1抛5
|
|
|
+ - 当素材总时长∈[240,∞)s 时,卡点抛留倍数为5x,即留1抛5
|
|
|
+ */
|
|
|
+ //1.1) 生成跳跃卡点的默认值
|
|
|
+ if(selectedTotalDuration >= 6 && selectedTotalDuration < 80){
|
|
|
+ lastJumpSpeedSelectIndex = 0
|
|
|
+ }else if(selectedTotalDuration >= 80 && selectedTotalDuration < 120){
|
|
|
+ lastJumpSpeedSelectIndex = 1
|
|
|
+ }else if(selectedTotalDuration >= 120 && selectedTotalDuration < 160){
|
|
|
+ lastJumpSpeedSelectIndex = 2
|
|
|
+ }else if(selectedTotalDuration >= 160 && selectedTotalDuration < 200){
|
|
|
+ lastJumpSpeedSelectIndex = 3
|
|
|
+ }else if(selectedTotalDuration >= 200 && selectedTotalDuration < 240){
|
|
|
+ lastJumpSpeedSelectIndex = 4
|
|
|
+ }else if(selectedTotalDuration >= 240){
|
|
|
+ lastJumpSpeedSelectIndex = 5
|
|
|
+ }
|
|
|
+
|
|
|
+ /*默认进入快慢速模式
|
|
|
+ - 当素材总时长∈[120,144]s 时,快慢速处理方式:快速为 6x,慢速为 1.2x,效果是快&快
|
|
|
+ - 当素材总时长∈[70,120)s 时,快慢速处理方式:快速为5x,慢速为 1x,效果是快&正常
|
|
|
+ - 当素材总时长∈[56,70)s 时,快慢速处理方式:快速为3x,慢速为 0.5x,效果是快&慢
|
|
|
+ - 当素材总时长∈[17.5,56)s 时,快慢速处理方式:快速为 2.4x,慢速为 0.4x,效果是快&慢
|
|
|
+ - 当素材总时长∈[10.5,17.5)s 时,快慢速处理方式:快速为 1.8x,慢速为 0.3x,效果是快&慢
|
|
|
+ - 当素材总时长∈(0,10.5)s 时,快慢速处理方式:快速为 1x,慢速为 0.2x,效果是正常&慢
|
|
|
+
|
|
|
+ */
|
|
|
+ //1.2)生成快慢速的默认值
|
|
|
+ if(selectedTotalDuration >= 120 && selectedTotalDuration <= 144){
|
|
|
+ lastSpeedSelectIndex = 0
|
|
|
+ }else if(selectedTotalDuration >= 70 && selectedTotalDuration < 120){
|
|
|
+ lastSpeedSelectIndex = 1
|
|
|
+ }else if(selectedTotalDuration >= 56 && selectedTotalDuration < 70){
|
|
|
+ lastSpeedSelectIndex = 2
|
|
|
+ }else if(selectedTotalDuration >= 17.5 && selectedTotalDuration < 56){
|
|
|
+ lastSpeedSelectIndex = 3
|
|
|
+ }else if(selectedTotalDuration >= 10.5 && selectedTotalDuration < 17.5){
|
|
|
+ lastSpeedSelectIndex = 4
|
|
|
+ }else if(selectedTotalDuration > 0 && selectedTotalDuration < 10.5){
|
|
|
+ lastSpeedSelectIndex = 5
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*
|
|
|
+ 文档规则 https://w42nne6hzg.feishu.cn/docs/doccnQZm1uCfkU4QtJb5fLxYk4d#
|
|
|
+ */
|
|
|
+ //2,根据所选择所有素材时长判断哪些功能不可能用,及进入默认模式
|
|
|
+ //全是图片
|
|
|
+ if(selectedDataCount == selectedImageDataCount){
|
|
|
+ BFLog(message: "全是图片 \(selectedDataCount) \(selectedImageDataCount)")
|
|
|
+ speedStuckBtn.isEnabled = false
|
|
|
+ //默认进入跳跃卡点模式
|
|
|
+ editModelClick(sender: jumpPointBtn)
|
|
|
+ speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex)
|
|
|
+
|
|
|
+ }else{
|
|
|
+
|
|
|
+ //1,根据素材输入确定可用模式及推荐模式
|
|
|
+ //1)0-6s时卡点不能使用, 纯图片时快慢速不可用
|
|
|
+ if(selectedTotalDuration > 0 && selectedTotalDuration < 6){
|
|
|
+ jumpPointBtn.isEnabled = false
|
|
|
+ }
|
|
|
+ //默认进入快慢速模式
|
|
|
+ if(selectedTotalDuration > 0 && selectedTotalDuration <= 144){
|
|
|
+
|
|
|
+ editModelClick(sender: speedStuckBtn)
|
|
|
+ speedSettingView.setSelectItem(index: lastSpeedSelectIndex)
|
|
|
+
|
|
|
+ }else{
|
|
|
+ //默认进入卡点模式
|
|
|
+ editModelClick(sender: jumpPointBtn)
|
|
|
+ speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex)
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
}
|