PQGPUImagePlayerView.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. //
  2. // PQGPUImagePlayer.swift
  3. // GPUImage_iOS
  4. //
  5. // Created by ak on 2020/8/27.
  6. // Copyright © 2020 Sunset Lake Software LLC. All rights reserved.
  7. // 功能:滤镜播放器 支持音频 https://juejin.im/post/6844904024760664078 这个有用
  8. import AVFoundation
  9. import AVKit
  10. import UIKit
  11. import ObjectMapper
  12. import BFCommonKit
  13. // import GPUImage
  14. struct AVAssetKey {
  15. static let tracks = "tracks"
  16. static let duration = "duration"
  17. static let metadata = "commonMetadata"
  18. }
  19. // 播放器状态
  20. public enum PQGPUImagePlayerViewStatus: Int {
  21. case playing = 10
  22. case pause = 20
  23. case stop = 30
  24. case error = 0
  25. case unknow = -1000
  26. }
  27. public class PQGPUImagePlayerView: UIView {
  28. public private(set) var playbackTime: TimeInterval = 0 {
  29. willSet {
  30. playbackTimeChangeClosure?(newValue)
  31. }
  32. }
  33. public var mCanverSize: CGSize = .zero
  34. // 自动隐藏边框
  35. public var isAutoHiden: Bool = false
  36. // 是否显示边框
  37. public var isShowLine: Bool = true
  38. // 播放进度
  39. public var playbackTimeChangeClosure: ((_ time: TimeInterval) -> Void)?
  40. // 参数说明:1,当前时间 2,总时长 3,进度
  41. public var progress: ((Double, Double, Double) -> Void)?
  42. /// 预览区域点击回调
  43. public var renderViewOnClickHandle: (() -> Void)?
  44. public private(set) var asset: AVAsset?
  45. public var duration: TimeInterval {
  46. return asset?.duration.seconds ?? 0
  47. }
  48. public private(set) var status: PQGPUImagePlayerViewStatus = .unknow {
  49. willSet {
  50. statusChangeClosure?(newValue)
  51. }
  52. }
  53. public var statusChangeClosure: ((_ status: PQGPUImagePlayerViewStatus) -> Void)?
  54. public private(set) var isReadyToPlay = false {
  55. willSet {
  56. assetLoadClosure?(newValue)
  57. }
  58. }
  59. public var assetLoadClosure: ((_ isReadyToPlay: Bool) -> Void)?
  60. /// Called when video finished
  61. /// This closure will not called if isLoop is true
  62. public var finishedClosure: (() -> Void)?
  63. /// Set this attribute to true will print debug info
  64. public var enableDebug = false {
  65. willSet {
  66. movie?.runBenchmark = newValue
  67. }
  68. }
  69. /// Setting this attribute before the end of the video works
  70. public var isLoop = false {
  71. willSet {
  72. movie?.loop = newValue
  73. }
  74. }
  75. /// The player will control the animationLayer of animation with the property `timeOffset`
  76. /// You can set up some animations in this layer like caption
  77. public var animationLayer: CALayer? {
  78. willSet {
  79. // Set speed to 0, use timeOffset to control the animation
  80. newValue?.speed = 0
  81. newValue?.timeOffset = playbackTime
  82. }
  83. didSet {
  84. oldValue?.removeFromSuperlayer()
  85. }
  86. }
  87. /// Add filters to this array and call updateAsset(_:) method
  88. public var filters: [ImageProcessingOperation] = []
  89. public var movie: PQMovieInput?
  90. public var speaker: SpeakerOutput?
  91. /// Volumn of original sounds in AVAsset
  92. public var originVolumn: Float = 1.0 {
  93. didSet {}
  94. }
  95. public var playerLayer: AVPlayerLayer?
  96. public var player: AVPlayer?
  97. public var playerEmptyView: UIImageView!
  98. public var borderLayer: CAShapeLayer?
  99. public var mPlayeTimeRange: CMTimeRange?
  100. var mStickers: [PQEditVisionTrackMaterialsModel]? {
  101. didSet {
  102. FilterLog(2, message: "设置线程为: \(Thread.current) \(OperationQueue.current?.underlyingQueue?.label as Any)")
  103. configCache(beginTime: mStickers?.first?.timelineIn ?? 0)
  104. }
  105. }
  106. // 是否显示时间条
  107. var showProgressLab: Bool = true
  108. // 缓存创建filter 防止 seek 100ms 慢
  109. @Atomic var cacheFilters: Array<PQBaseFilter> = Array()
  110. // 缓存个数 XXXX 经过测试如果是4K 视频解码器不能创建太多,4是可以工作
  111. var cacheFiltersMaxCount: Int = 8
  112. /// Use serial queue to ensure that the picture is smooth
  113. var createFiltersQueue: DispatchQueue!
  114. //是否显示高斯
  115. public var showGaussianBlur:Bool = false
  116. //是否使用AVPlayer播放音乐
  117. public var isUsedAVPlayer:Bool = false
  118. // 渲染区view
  119. private lazy var renderView: RenderView = {
  120. let view = RenderView()
  121. view.backgroundColor = BFConfig.shared.styleBackGroundColor
  122. view.frame = self.bounds
  123. view.delegate = self
  124. let tap = UITapGestureRecognizer(target: self, action: #selector(RenderViewOnclick))
  125. view.addGestureRecognizer(tap)
  126. view.backgroundRenderColor = Color.init(red: Float(BFConfig.shared.styleBackGroundColor.rgbaf[0]), green: Float(BFConfig.shared.styleBackGroundColor.rgbaf[1]), blue: Float(BFConfig.shared.styleBackGroundColor.rgbaf[2]))
  127. return view
  128. }()
  129. // 暂停播放view
  130. lazy var playView: UIImageView = {
  131. let view = UIImageView(frame: CGRect(x: (self.frame.size.width - self.frame.size.height / 3.6) / 2, y: (self.frame.size.height - self.frame.size.height / 3.6) / 2, width: self.frame.size.height / 3.6, height: self.frame.size.height / 3.6))
  132. // view.tintColor = UIColor.white
  133. view.image = UIImage.moduleImage(named: "gpuplayBtn", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate)
  134. view.tintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
  135. view.isHidden = true
  136. return view
  137. }()
  138. // 暂停播放view
  139. lazy var playMaskView: UIView = {
  140. let playMaskView = UIView.init()
  141. playMaskView.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.5)
  142. playMaskView.isUserInteractionEnabled = false
  143. playMaskView.isHidden = true
  144. return playMaskView
  145. }()
  146. // 播放进度/总时长
  147. lazy var progressLab: UILabel = {
  148. let titleLab = UILabel(frame: CGRect(x: (self.frame.size.width - 140) / 2, y: 0, width: 140, height: 12))
  149. titleLab.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  150. titleLab.textColor = UIColor.white
  151. titleLab.textAlignment = .center
  152. titleLab.text = ""
  153. titleLab.layer.shadowColor = UIColor.black.cgColor
  154. titleLab.layer.shadowOpacity = 0.3
  155. titleLab.layer.shadowOffset = .zero
  156. titleLab.layer.shadowRadius = 1
  157. // titleLab.backgroundColor = UIColor.hexColor(hexadecimal: "#FFFFFF",alpha: 0.3)
  158. // titleLab.addCorner(corner:7)
  159. return titleLab
  160. }()
  161. lazy var tipLab: UILabel = {
  162. let tipLab = UILabel(frame: CGRect(x: (self.frame.size.width - 100) / 2, y: (self.frame.size.height - 14) / 2, width: 100, height: 14))
  163. tipLab.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  164. tipLab.textColor = UIColor.white
  165. tipLab.textAlignment = .center
  166. tipLab.text = "资源加载中..."
  167. tipLab.layer.shadowColor = UIColor.white.cgColor
  168. tipLab.layer.shadowOpacity = 0.5
  169. tipLab.layer.shadowOffset = .zero
  170. tipLab.layer.shadowRadius = 1
  171. tipLab.isHidden = true
  172. return tipLab
  173. }()
  174. //进度的开始时间
  175. var showProgressStartTime:Float = 0.0
  176. required public init?(coder _: NSCoder) {
  177. fatalError("init(coder:) has not been implemented")
  178. }
  179. override public init(frame: CGRect) {
  180. super.init(frame: frame)
  181. addSubview(renderView)
  182. addSubview(progressLab)
  183. addSubview(playMaskView)
  184. addSubview(playView)
  185. backgroundColor = BFConfig.shared.styleBackGroundColor
  186. playerEmptyView = UIImageView(frame: bounds)
  187. playerEmptyView.backgroundColor = .black
  188. playerEmptyView.image = UIImage.moduleImage(named: "playEmpty", moduleName: "BFFramework",isAssets: false)
  189. playerEmptyView.contentMode = .center
  190. addSubview(playerEmptyView)
  191. addSubview(tipLab)
  192. if #available(iOS 10.0, *) {
  193. createFiltersQueue = DispatchQueue(label: "PQ.moveFiler.seeking111", qos: .default, attributes: .initiallyInactive, autoreleaseFrequency: .never, target: nil)
  194. } else {
  195. createFiltersQueue = DispatchQueue(label: "PQ.moveFiler.seeking111", qos: .userInteractive, attributes: [], autoreleaseFrequency: .inherit, target: nil)
  196. }
  197. if #available(iOS 10.0, *) {
  198. createFiltersQueue.activate()
  199. }
  200. }
  201. func showBorderLayer() {
  202. if borderLayer != nil {
  203. borderLayer?.removeFromSuperlayer()
  204. }
  205. // 线条颜色
  206. borderLayer = CAShapeLayer()
  207. borderLayer?.strokeColor = UIColor.hexColor(hexadecimal: "#FFFFFF").cgColor
  208. borderLayer?.fillColor = nil
  209. borderLayer?.path = UIBezierPath(rect: CGRect(x: 1, y: 1, width: bounds.width - 2, height: bounds.height - 2)).cgPath
  210. borderLayer?.frame = bounds
  211. borderLayer?.lineWidth = 2.0
  212. borderLayer?.lineCap = .round
  213. // 第一位是 线条长度 第二位是间距 nil时为实线
  214. if borderLayer != nil {
  215. renderView.layer.addSublayer(borderLayer!)
  216. }
  217. if isAutoHiden {
  218. borderLayer?.opacity = 0
  219. let groupAnimation = CAAnimationGroup()
  220. groupAnimation.beginTime = CACurrentMediaTime()
  221. groupAnimation.duration = 1
  222. groupAnimation.fillMode = .forwards
  223. groupAnimation.isRemovedOnCompletion = true
  224. groupAnimation.repeatCount = 3
  225. let opacity = CABasicAnimation(keyPath: "opacity")
  226. opacity.fromValue = 0
  227. opacity.toValue = 1
  228. opacity.isRemovedOnCompletion = true
  229. let opacity2 = CABasicAnimation(keyPath: "opacity")
  230. opacity2.fromValue = 1
  231. opacity2.toValue = 0
  232. opacity2.isRemovedOnCompletion = false
  233. groupAnimation.animations = [opacity, opacity2]
  234. borderLayer?.add(groupAnimation, forKey: nil)
  235. }
  236. }
  237. // 设置画布比例
  238. public func resetCanvasFrame(frame: CGRect) {
  239. if self.frame.equalTo(frame) {
  240. FilterLog(2, message: "新老值一样,不重置")
  241. return
  242. }
  243. self.frame = frame
  244. mCanverSize = frame.size
  245. if isShowLine {
  246. showBorderLayer()
  247. }
  248. FilterLog(2, message: "new frame is \(frame)")
  249. renderView.isHidden = true
  250. renderView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
  251. renderView.resatSize()
  252. playerEmptyView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
  253. playMaskView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
  254. tipLab.frame = CGRect(x: (self.frame.size.width - 100) / 2, y: (self.frame.size.height - 14) / 2, width: 100, height: 14)
  255. progressLab.frame = CGRect(x: (self.frame.size.width - 140) / 2, y: 8, width: 140, height: 14)
  256. let bord = frame.size.width > frame.size.height ? CGFloat(60) : CGFloat(60)
  257. playView.frame = CGRect(x: (CGFloat(frame.size.width) - bord) / 2 , y: (CGFloat(frame.size.height) - bord) / 2, width: bord, height: bord)
  258. // playView.frame = CGRect(x: (self.frame.size.width - self.frame.size.height / 3.6) / 2, y: (self.frame.size.height - self.frame.size.height / 3.6) / 2, width: self.frame.size.height / 3.6, height: self.frame.size.height / 3.6)
  259. }
  260. override public func layoutSubviews() {
  261. super.layoutSubviews()
  262. }
  263. @objc func RenderViewOnclick() {
  264. if status == .playing {
  265. playView.isHidden = false
  266. playMaskView.isHidden = false
  267. pause()
  268. } else if status == .stop || status == .pause {
  269. playView.isHidden = true
  270. playMaskView.isHidden = true
  271. movie?.resume()
  272. speaker?.start()
  273. status = .playing
  274. }
  275. if renderViewOnClickHandle != nil {
  276. renderViewOnClickHandle!()
  277. }
  278. }
  279. func showPlayBtn(isHidden: Bool) {
  280. playView.isHidden = isHidden
  281. playMaskView.isHidden = isHidden
  282. }
  283. deinit {
  284. stop()
  285. movie = nil
  286. speaker = nil
  287. FilterLog(1, message: "play view release")
  288. }
  289. /// XXXX 这里的 URL 使用的是全路径 ,如果不是全的会 crash ,方便复用 (不用处理业务的文件放在哪里)
  290. public func updateAsset(_ url: URL, videoComposition: AVVideoComposition? = nil, audioMixModel: PQVoiceModel? = nil, videoStickers: [PQEditVisionTrackMaterialsModel]? = nil,originMusicDuration:Float = 0,lastPoint:Float = 0,clipAudioRange: CMTimeRange = CMTimeRange.zero ,isUsedAVPlayer:Bool = false) {
  291. self.isUsedAVPlayer = isUsedAVPlayer
  292. // 每次初始化的时候设置初始值 为 nIl
  293. var audioMix: AVMutableAudioMix?
  294. var composition: AVMutableComposition?
  295. let asset = AVURLAsset(url: url, options: nil)
  296. FilterLog(1, message: "播放器初始化的音频时长\(asset.duration.seconds) url is \(url),最终使用时长\(originMusicDuration),裁剪范围\(CMTimeGetSeconds(clipAudioRange.start)) 到 \(CMTimeGetSeconds(clipAudioRange.end))")
  297. self.asset = asset
  298. if (audioMixModel != nil && audioMixModel?.localPath != nil) || (videoStickers != nil && (videoStickers?.count ?? 0) > 0 || originMusicDuration != 0) {
  299. FilterLog(2, message: "有参加混音的数据。")
  300. (audioMix, composition) = PQPlayerViewModel.setupAudioMix(originAsset: asset, bgmData: audioMixModel, videoStickers: videoStickers,originMusicDuration:originMusicDuration,clipAudioRange: clipAudioRange)
  301. } else {
  302. audioMix = nil
  303. }
  304. isReadyToPlay = false
  305. asset.loadValuesAsynchronously(forKeys: ["tracks", "duration", "commonMetadata"]) { [weak self] in
  306. guard let strongSelf = self else { return }
  307. let tracksStatus = strongSelf.asset?.statusOfValue(forKey: AVAssetKey.tracks, error: nil) ?? .unknown
  308. let durationStatus = strongSelf.asset?.statusOfValue(forKey: AVAssetKey.duration, error: nil) ?? .unknown
  309. strongSelf.isReadyToPlay = tracksStatus == .loaded && durationStatus == .loaded
  310. }
  311. var audioSettings: [String: Any] = [
  312. AVFormatIDKey: kAudioFormatLinearPCM,
  313. ]
  314. // if #available(iOS 14.0, *) {
  315. audioSettings[AVLinearPCMIsFloatKey] = false
  316. audioSettings[AVLinearPCMBitDepthKey] = 16
  317. // }
  318. do {
  319. if composition != nil {
  320. FilterLog(2, message: "composition 方式初始化")
  321. movie = try PQMovieInput(asset: composition!, videoComposition: videoComposition, audioMix: audioMix, playAtActualSpeed: true, loop: isLoop, audioSettings: audioSettings)
  322. // movie?.exportAudioUrl = url // clipAudioRange
  323. var ranges = Array<CMTimeRange>()
  324. if CMTimeGetSeconds(clipAudioRange.duration) == 0 {
  325. let range = CMTimeRange(start: CMTime.zero, duration: asset.duration)
  326. ranges.append(range)
  327. }else{
  328. ranges.append(clipAudioRange)
  329. }
  330. movie?.configAVPlayer(assetUrl: url, ranges: ranges)
  331. } else {
  332. movie = try PQMovieInput(url: url, playAtActualSpeed: true, loop: isLoop, audioSettings: audioSettings)
  333. /* 测试代码
  334. let audioDecodeSettings = [AVFormatIDKey:kAudioFormatLinearPCM]
  335. let bundleURL = Bundle.main.resourceURL!
  336. let movieURL = URL(string:"11111.mp4", relativeTo:bundleURL)!
  337. movie = try MovieInput(url:movieURL, playAtActualSpeed:true, loop:true, audioSettings:audioDecodeSettings)
  338. */
  339. }
  340. movie!.runBenchmark = false
  341. movie!.synchronizedEncodingDebug = false
  342. movie!.isUsedAVPlayer = isUsedAVPlayer
  343. } catch {
  344. status = .error
  345. if enableDebug {
  346. debugPrint(error)
  347. }
  348. }
  349. guard let movie = movie else { return }
  350. movie.progress = { [weak self] currTime, duration, prgressValue in
  351. guard let strongSelf = self else { return }
  352. // FilterLog(1, message: " movie 进度\(currTime)")
  353. strongSelf.changeFilter(currTime: currTime)
  354. strongSelf.progress?(currTime, duration, prgressValue)
  355. DispatchQueue.main.async {
  356. strongSelf.playbackTime = currTime
  357. // Non-main thread change this property is not valid
  358. strongSelf.animationLayer?.timeOffset = strongSelf.playbackTime
  359. if strongSelf.showProgressLab {
  360. if(strongSelf.showProgressStartTime == 0 ){
  361. strongSelf.showProgressStartTime = Float(CMTimeGetSeconds(strongSelf.movie?.startTime ?? .zero))
  362. }
  363. if duration < 1 {
  364. strongSelf.progressLab.text = "\((currTime - Double(CMTimeGetSeconds(strongSelf.movie?.startTime ?? .zero))).formatDurationToHMS()) / 00:01"
  365. } else {
  366. var showTime = currTime - Double(strongSelf.showProgressStartTime)
  367. if (showTime < 0){
  368. showTime = 0
  369. }
  370. strongSelf.progressLab.text = "\(showTime.formatDurationToHMS()) / \( (duration - Double(strongSelf.showProgressStartTime)).formatDurationToHMS())"
  371. }
  372. }
  373. }
  374. }
  375. movie.completion = { [weak self] in
  376. guard let strongSelf = self else { return }
  377. //缓存已经用完,重新初始化缓存
  378. if(strongSelf.filters.count == 0){
  379. strongSelf.configCache(beginTime: strongSelf.mStickers?.first?.timelineIn ?? 0)
  380. }
  381. DispatchQueue.main.async {
  382. strongSelf.status = .stop
  383. strongSelf.finishedClosure?()
  384. strongSelf.showPlayBtn(isHidden: false)
  385. if(strongSelf.progress != nil){
  386. strongSelf.progress!(0,0,1)
  387. }
  388. }
  389. }
  390. speaker = SpeakerOutput()
  391. movie.audioEncodingTarget = speaker
  392. applyFilters()
  393. }
  394. /// 初始化缓存,默认选创建 cacheFiltersMaxCount 个缓存 filterrs
  395. /// - Parameter beginTime: 开始缓存的开始时间,用在 seek操作时 老的缓存已经无效不能在使用了
  396. func configCache(beginTime: Float64 ) {
  397. cacheFilters.removeAll()
  398. FilterLog(2, message: "原素材 总数:\(mStickers?.count ?? 0) ")
  399. if mStickers?.count ?? 0 > 0 {
  400. for (index, currentSticker) in mStickers!.enumerated() {
  401. FilterLog(message: "mStickers timelinein:\(currentSticker.timelineIn) timelineout: \(currentSticker.timelineOut) index : \(index)")
  402. //到达最大缓存数退出
  403. if cacheFilters.count == cacheFiltersMaxCount {
  404. break
  405. }
  406. //小于缓存的开始时间继续查找
  407. if(currentSticker.timelineOut < beginTime){
  408. continue
  409. }
  410. var showFitler: PQBaseFilter?
  411. if currentSticker.type == StickerType.VIDEO.rawValue {
  412. showFitler = PQMovieFilter(movieSticker: currentSticker)
  413. } else if currentSticker.type == StickerType.IMAGE.rawValue {
  414. showFitler = PQImageFilter(sticker: currentSticker, isExport: (movie?.mIsExport) ?? false, showUISize: mCanverSize)
  415. (showFitler as? PQImageFilter)?.isPointModel = ((mStickers?.count ?? 0) > 0)
  416. }
  417. if showFitler != nil {
  418. FilterLog(message: " 加入到缓存 的 filter timelinein:\(currentSticker.timelineIn) timelineout: \(currentSticker.timelineOut) in :\(currentSticker.model_in) out: \(currentSticker.out) index : \(index)")
  419. cacheFilters.append(showFitler!)
  420. }
  421. }
  422. DispatchQueue.global().async {[weak self] in
  423. if let strongSelf = self {
  424. for (index, filter) in strongSelf.cacheFilters.enumerated() {
  425. FilterLog(2, message: " 初始化 config create currentSticker timelinein \(String(describing: filter.stickerInfo?.timelineIn)) timelineout \(String(describing: filter.stickerInfo?.timelineOut)) in :\(String(describing: filter.stickerInfo?.model_in)) out \(String(describing: filter.stickerInfo?.out)) index\(index)")
  426. }
  427. }
  428. }
  429. if(cacheFilters.first != nil){
  430. movie?.removeAllTargets()
  431. let showFilter: PQBaseFilter = cacheFilters.first!
  432. movie?.addTarget(showFilter, atTargetIndex: 0)
  433. showFilter.addTarget(renderView, atTargetIndex: 0)
  434. }
  435. }
  436. }
  437. //创建下一个filter 数据
  438. func createNextFilter() {
  439. FilterLog(2, message: "加入前 当前的缓存个数为: \(cacheFilters.count) maxCount \(cacheFiltersMaxCount) 最后一个显示时间 \(String(describing: cacheFilters.last?.stickerInfo?.timelineIn))")
  440. if cacheFilters.count <= cacheFiltersMaxCount {
  441. let showIndex = mStickers?.firstIndex(where: { (sticker) -> Bool in
  442. (cacheFilters.last?.stickerInfo == sticker)
  443. })
  444. FilterLog(2, message: "当前显示的showIndex: \(String(describing: showIndex))")
  445. if ((showIndex ?? 0) + 1) < (mStickers?.count ?? 0) {
  446. let currentSticker = mStickers?[(showIndex ?? 0) + 1]
  447. if currentSticker != nil {
  448. var showFitler: PQBaseFilter?
  449. if currentSticker!.type == StickerType.VIDEO.rawValue {
  450. showFitler = PQMovieFilter(movieSticker: currentSticker!)
  451. } else if currentSticker!.type == StickerType.IMAGE.rawValue {
  452. showFitler = PQImageFilter(sticker: currentSticker!, isExport: (movie?.mIsExport) ?? false, showUISize: mCanverSize)
  453. (showFitler as? PQImageFilter)?.isPointModel = ((mStickers?.count ?? 0) > 0)
  454. }
  455. if showFitler != nil {
  456. cacheFilters.append(showFitler!)
  457. }
  458. }else{
  459. FilterLog(2, message: "缓存数据加入不成功!!!!!")
  460. }
  461. }
  462. FilterLog(2, message: "加入后 当前的缓存个数为: \(cacheFilters.count) maxCount \(cacheFiltersMaxCount) 最后一个显示时间 \(String(describing: cacheFilters.last?.stickerInfo?.timelineIn))")
  463. }
  464. }
  465. /// 按时间从缓存中取出要显示的filter
  466. /// - Parameter currTime: 当前播放时间
  467. func changeFilter(currTime: Float64) {
  468. // let starts:CFTimeInterval = CFAbsoluteTimeGetCurrent()
  469. FilterLog(message: " 要查找的 currTime is \(currTime)")
  470. //1,删除已经显示过的 filter
  471. self.cacheFilters.removeAll(where: {(filter) -> Bool in
  472. (currTime > (filter.stickerInfo?.timelineOut ?? 0.0))
  473. })
  474. // 2,找出一个要显示的 fitler
  475. let showIndex = cacheFilters.firstIndex(where: { (filter) -> Bool in
  476. (currTime >= (filter.stickerInfo?.timelineIn ?? 0.0) && currTime <= (filter.stickerInfo?.timelineOut ?? 0.0))
  477. })
  478. if(showIndex == nil){
  479. FilterLog(2, message: "缓存没有查找到?出现数据错误!!!!")
  480. return
  481. }
  482. let showFilter: PQBaseFilter = cacheFilters[showIndex ?? 0]
  483. FilterLog(2, message: "缓存操作 查找到命中的显示是为:\(currTime) 缓存数据timeline in :\(showFilter.stickerInfo?.timelineIn ?? 0.0)) timelineOut:\(showFilter.stickerInfo?.timelineOut ?? 0.0) in:\(showFilter.stickerInfo?.model_in ?? 0.0) out:\(showFilter.stickerInfo?.out ?? 0.0) 缓存数 \(cacheFilters.count) index: \(String(describing: showIndex))")
  484. if(!(showFilter.isShow)){
  485. FilterLog(2, message: "showIndex当前时间为 \(currTime) showIndex is \(String(describing: showIndex)) 显示 filter timelineIn is: \(String(describing: showFilter.stickerInfo?.timelineIn)) timelineOut is: \(String(describing: showFilter.stickerInfo?.timelineOut))")
  486. showFilter.isShow = true
  487. movie!.removeAllTargets()
  488. //为了优化性能只有素材宽高比和画面宽高比不一样时才做高斯
  489. //原图的比例
  490. let stickerAspectRatio = String(format: "%.6f", (showFilter.stickerInfo?.width ?? 0.0 ) / (showFilter.stickerInfo?.height ?? 0.0))
  491. //画面的比例
  492. let canverAspectRatio = String(format: "%.6f",(movie?.mShowVidoSize.width ?? 0.0) / (movie?.mShowVidoSize.height ?? 0.0))
  493. if(showFilter.stickerInfo?.type == StickerType.IMAGE.rawValue && showGaussianBlur && Float(stickerAspectRatio) != Float(canverAspectRatio)){
  494. FilterLog(2, message: "显示图片filter")
  495. // //高斯层
  496. let blurStickerModel:PQEditVisionTrackMaterialsModel? = showFilter.stickerInfo?.copy() as? PQEditVisionTrackMaterialsModel
  497. blurStickerModel?.canvasFillType = stickerContentMode.aspectFillStr.rawValue
  498. if blurStickerModel == nil {
  499. FilterLog(2, message: "显示图片filter blurStickerModel is nil")
  500. return
  501. }
  502. let showGaussianFitler:PQBaseFilter = PQImageFilter(sticker: blurStickerModel!, isExport: (movie?.mIsExport) ?? false, showUISize: mCanverSize)
  503. (showGaussianFitler as? PQImageFilter)?.isPointModel = ((mStickers?.count ?? 0) > 0)
  504. let iosb:GaussianBlur = GaussianBlur.init()
  505. iosb.blurRadiusInPixels = 20
  506. showGaussianFitler.addTarget(iosb)
  507. self.movie?.addTarget(showGaussianFitler, atTargetIndex: 0)
  508. iosb.addTarget(showFilter,atTargetIndex: 0)
  509. showFilter.addTarget(self.renderView as ImageConsumer, atTargetIndex: 0)
  510. FilterLog(2, message: "filter 添加成功 注意是否添加成功。")
  511. // }
  512. }else{
  513. movie?.addTarget(showFilter, atTargetIndex: 0)
  514. showFilter.addTarget(renderView, atTargetIndex: 0)
  515. }
  516. self.createFiltersQueue.async {
  517. self.createNextFilter()
  518. }
  519. }else{
  520. FilterLog(2, message: " 添加过了 currTime is \(currTime) timelineIn:\(showFilter.stickerInfo?.timelineIn ?? 0.0)")
  521. }
  522. }
  523. /// 设置 filter 是否为 seek 状态
  524. func setEnableSeek(isSeek: Bool) {
  525. for filter in filters {
  526. (filter as? PQBaseFilter)?.enableSeek = isSeek
  527. }
  528. }
  529. private func applyFilters() {
  530. guard let movie = movie else { return }
  531. movie.removeAllTargets()
  532. var currentTarget: ImageSource = movie
  533. filters.forEach {
  534. let f = $0
  535. currentTarget.addTarget(f, atTargetIndex: 0)
  536. currentTarget = f
  537. }
  538. currentTarget.addTarget(renderView, atTargetIndex: 0)
  539. }
  540. }
  541. // MARK: Player control
  542. public extension PQGPUImagePlayerView {
  543. /// 开始播放
  544. /// - Parameter pauseFirstFrame: 是否暂停到第一帧
  545. func play(pauseFirstFrame: Bool = false, playeTimeRange: CMTimeRange = CMTimeRange()) {
  546. DispatchQueue.main.async {
  547. self.playerEmptyView.isHidden = true
  548. self.playView.isHidden = !pauseFirstFrame
  549. self.playMaskView.isHidden = !pauseFirstFrame
  550. self.renderView.isHidden = false
  551. self.progressLab.isHidden = false
  552. }
  553. // guard status != .playing else {
  554. // FilterLog(2, message: "已经是播放状态")
  555. // return
  556. // }
  557. // 如果没有设置开始结束时长 使用默认音频总时长(创作工具就不会传值)
  558. if CMTIMERANGE_IS_INVALID(playeTimeRange) {
  559. let endTime = CMTime(value: CMTimeValue(CMTimeGetSeconds(asset?.duration ?? .zero) * 600), timescale: 600)
  560. mPlayeTimeRange = CMTimeRange(start: .zero, end: endTime)
  561. } else {
  562. mPlayeTimeRange = playeTimeRange
  563. }
  564. // 清空音频缓存
  565. speaker?.clearBuffer()
  566. movie?.start(timeRange: mPlayeTimeRange ?? CMTimeRange())
  567. speaker?.start()
  568. status = pauseFirstFrame ? .pause : .playing
  569. showProgressStartTime = 0
  570. }
  571. // 快进
  572. func seek(to time: CMTime) {
  573. mPlayeTimeRange?.start = time
  574. play(pauseFirstFrame: false, playeTimeRange: mPlayeTimeRange ?? .zero)
  575. }
  576. // 暂停
  577. func pause() {
  578. guard status != .pause else {
  579. return
  580. }
  581. movie?.pause() // 可能会引起crash: configureThread()里timebaseInfo为0,除法出错
  582. speaker?.pause()
  583. status = .pause
  584. showPlayBtn(isHidden: false)
  585. }
  586. // 停止f解码状态
  587. func stop() {
  588. // guard status != .stop else {
  589. // return
  590. // }
  591. movie?.removeAllTargets()
  592. movie?.cancel()
  593. speaker?.cancel()
  594. status = .stop
  595. }
  596. // 清空播放器状态,到空状态
  597. func clearPlayerView() {
  598. playerEmptyView.isHidden = false
  599. renderView.isHidden = true
  600. progressLab.isHidden = true
  601. }
  602. // 显示提示文字
  603. func showTip(show: Bool) {
  604. FilterLog(2, message: "showTip \(show)")
  605. tipLab.isHidden = !show
  606. if show {
  607. playerEmptyView.isHidden = true
  608. renderView.isHidden = true
  609. progressLab.isHidden = true
  610. }
  611. }
  612. }
  613. // MARK: Filter 操作
  614. public extension PQGPUImagePlayerView {
  615. // 添加 filter
  616. func appendFilter(_ filter: ImageProcessingOperation) {
  617. filters.append(filter)
  618. }
  619. // 添加一组filters
  620. func appendFilters(_ newFilters: [ImageProcessingOperation]) {
  621. filters = filters + newFilters
  622. }
  623. // 移除所有filter
  624. func removeAllFilters() {
  625. filters.removeAll()
  626. }
  627. // 重置所有 filer
  628. func appendFiltersClearOldFilter(_ newFilters: [ImageProcessingOperation]) {
  629. filters.removeAll()
  630. filters = newFilters
  631. }
  632. }
  633. // MARK: - RenderViewDelegate
  634. extension PQGPUImagePlayerView: RenderViewDelegate{
  635. public func willDisplayFramebuffer(renderView _: RenderView, framebuffer _: Framebuffer) {
  636. FilterLog(2, message: "willDisplayFramebuffer")
  637. }
  638. public func didDisplayFramebuffer(renderView _: RenderView, framebuffer: Framebuffer) {
  639. FilterLog(2, message: "didDisplayFramebuffer")
  640. }
  641. public func shouldDisplayNextFramebufferAfterMainThreadLoop() -> Bool {
  642. FilterLog(2, message: "didDisplayFramebuffer")
  643. return false
  644. }
  645. }