123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- //
- // PQGPUImagePlayer.swift
- // GPUImage_iOS
- //
- // Created by ak on 2020/8/27.
- // Copyright © 2020 Sunset Lake Software LLC. All rights reserved.
- // 功能:滤镜播放器 支持音频 https://juejin.im/post/6844904024760664078 这个有用
- import AVFoundation
- import AVKit
- import UIKit
- import ObjectMapper
- import BFCommonKit
- // import GPUImage
- struct AVAssetKey {
- static let tracks = "tracks"
- static let duration = "duration"
- static let metadata = "commonMetadata"
- }
- // 播放器状态
- public enum PQGPUImagePlayerViewStatus: Int {
- case playing = 10
- case pause = 20
- case stop = 30
- case error = 0
- case unknow = -1000
- }
- public class PQGPUImagePlayerView: UIView {
-
- public private(set) var playbackTime: TimeInterval = 0 {
- willSet {
- playbackTimeChangeClosure?(newValue)
- }
- }
- public var mCanverSize: CGSize = .zero
- // 自动隐藏边框
- public var isAutoHiden: Bool = false
- // 是否显示边框
- public var isShowLine: Bool = true
- // 播放进度
- public var playbackTimeChangeClosure: ((_ time: TimeInterval) -> Void)?
- // 参数说明:1,当前时间 2,总时长 3,进度
- public var progress: ((Double, Double, Double) -> Void)?
- /// 预览区域点击回调
- public var renderViewOnClickHandle: (() -> Void)?
- public private(set) var asset: AVAsset?
- public var duration: TimeInterval {
- return asset?.duration.seconds ?? 0
- }
- public private(set) var status: PQGPUImagePlayerViewStatus = .unknow {
- willSet {
- statusChangeClosure?(newValue)
- }
- }
- public var statusChangeClosure: ((_ status: PQGPUImagePlayerViewStatus) -> Void)?
- public private(set) var isReadyToPlay = false {
- willSet {
- assetLoadClosure?(newValue)
- }
- }
- public var assetLoadClosure: ((_ isReadyToPlay: Bool) -> Void)?
- /// Called when video finished
- /// This closure will not called if isLoop is true
- public var finishedClosure: (() -> Void)?
- /// Set this attribute to true will print debug info
- public var enableDebug = false {
- willSet {
- movie?.runBenchmark = newValue
- }
- }
- /// Setting this attribute before the end of the video works
- public var isLoop = false {
- willSet {
- movie?.loop = newValue
- }
- }
- /// The player will control the animationLayer of animation with the property `timeOffset`
- /// You can set up some animations in this layer like caption
- public var animationLayer: CALayer? {
- willSet {
- // Set speed to 0, use timeOffset to control the animation
- newValue?.speed = 0
- newValue?.timeOffset = playbackTime
- }
- didSet {
- oldValue?.removeFromSuperlayer()
- }
- }
- /// Add filters to this array and call updateAsset(_:) method
- public var filters: [ImageProcessingOperation] = []
- public var movie: PQMovieInput?
- public var speaker: SpeakerOutput?
- /// Volumn of original sounds in AVAsset
- public var originVolumn: Float = 1.0 {
- didSet {}
- }
- public var playerLayer: AVPlayerLayer?
- public var player: AVPlayer?
- public var playerEmptyView: UIImageView!
- public var borderLayer: CAShapeLayer?
- public var mPlayeTimeRange: CMTimeRange?
- var mStickers: [PQEditVisionTrackMaterialsModel]? {
- didSet {
-
- FilterLog(2, message: "设置线程为: \(Thread.current) \(OperationQueue.current?.underlyingQueue?.label as Any)")
-
- configCache(beginTime: mStickers?.first?.timelineIn ?? 0)
- }
- }
-
- // 是否显示时间条
- var showProgressLab: Bool = true
- // 缓存创建filter 防止 seek 100ms 慢
- @Atomic var cacheFilters: Array<PQBaseFilter> = Array()
- // 缓存个数 XXXX 经过测试如果是4K 视频解码器不能创建太多,4是可以工作
- var cacheFiltersMaxCount: Int = 8
-
- /// Use serial queue to ensure that the picture is smooth
- var createFiltersQueue: DispatchQueue!
-
- //是否显示高斯
- public var showGaussianBlur:Bool = false
-
- //是否使用AVPlayer播放音乐
- public var isUsedAVPlayer:Bool = false
- // 渲染区view
- private lazy var renderView: RenderView = {
- let view = RenderView()
- view.backgroundColor = BFConfig.shared.styleBackGroundColor
- view.frame = self.bounds
- view.delegate = self
- let tap = UITapGestureRecognizer(target: self, action: #selector(RenderViewOnclick))
- view.addGestureRecognizer(tap)
-
- 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]))
- return view
- }()
- // 暂停播放view
- lazy var playView: UIImageView = {
- 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))
- // view.tintColor = UIColor.white
- view.image = UIImage.moduleImage(named: "gpuplayBtn", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate)
- view.tintColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
- view.isHidden = true
- return view
- }()
-
- // 暂停播放view
- lazy var playMaskView: UIView = {
- let playMaskView = UIView.init()
- playMaskView.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.5)
- playMaskView.isUserInteractionEnabled = false
- playMaskView.isHidden = true
- return playMaskView
- }()
- // 播放进度/总时长
- lazy var progressLab: UILabel = {
- let titleLab = UILabel(frame: CGRect(x: (self.frame.size.width - 140) / 2, y: 0, width: 140, height: 12))
- titleLab.font = UIFont.systemFont(ofSize: 12, weight: .medium)
- titleLab.textColor = UIColor.white
- titleLab.textAlignment = .center
- titleLab.text = ""
- titleLab.layer.shadowColor = UIColor.black.cgColor
- titleLab.layer.shadowOpacity = 0.3
- titleLab.layer.shadowOffset = .zero
- titleLab.layer.shadowRadius = 1
- // titleLab.backgroundColor = UIColor.hexColor(hexadecimal: "#FFFFFF",alpha: 0.3)
- // titleLab.addCorner(corner:7)
- return titleLab
- }()
- lazy var tipLab: UILabel = {
- let tipLab = UILabel(frame: CGRect(x: (self.frame.size.width - 100) / 2, y: (self.frame.size.height - 14) / 2, width: 100, height: 14))
- tipLab.font = UIFont.systemFont(ofSize: 14, weight: .medium)
- tipLab.textColor = UIColor.white
- tipLab.textAlignment = .center
- tipLab.text = "资源加载中..."
- tipLab.layer.shadowColor = UIColor.white.cgColor
- tipLab.layer.shadowOpacity = 0.5
- tipLab.layer.shadowOffset = .zero
- tipLab.layer.shadowRadius = 1
- tipLab.isHidden = true
- return tipLab
- }()
- //进度的开始时间
- var showProgressStartTime:Float = 0.0
- required public init?(coder _: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override public init(frame: CGRect) {
- super.init(frame: frame)
-
- addSubview(renderView)
- addSubview(progressLab)
- addSubview(playMaskView)
- addSubview(playView)
-
- backgroundColor = BFConfig.shared.styleBackGroundColor
- playerEmptyView = UIImageView(frame: bounds)
- playerEmptyView.backgroundColor = .black
- playerEmptyView.image = UIImage.moduleImage(named: "playEmpty", moduleName: "BFFramework",isAssets: false)
- playerEmptyView.contentMode = .center
- addSubview(playerEmptyView)
- addSubview(tipLab)
-
- if #available(iOS 10.0, *) {
- createFiltersQueue = DispatchQueue(label: "PQ.moveFiler.seeking111", qos: .default, attributes: .initiallyInactive, autoreleaseFrequency: .never, target: nil)
- } else {
- createFiltersQueue = DispatchQueue(label: "PQ.moveFiler.seeking111", qos: .userInteractive, attributes: [], autoreleaseFrequency: .inherit, target: nil)
- }
- if #available(iOS 10.0, *) {
- createFiltersQueue.activate()
- }
- }
- func showBorderLayer() {
- if borderLayer != nil {
- borderLayer?.removeFromSuperlayer()
- }
- // 线条颜色
- borderLayer = CAShapeLayer()
- borderLayer?.strokeColor = UIColor.hexColor(hexadecimal: "#FFFFFF").cgColor
- borderLayer?.fillColor = nil
- borderLayer?.path = UIBezierPath(rect: CGRect(x: 1, y: 1, width: bounds.width - 2, height: bounds.height - 2)).cgPath
- borderLayer?.frame = bounds
- borderLayer?.lineWidth = 2.0
- borderLayer?.lineCap = .round
- // 第一位是 线条长度 第二位是间距 nil时为实线
- if borderLayer != nil {
- renderView.layer.addSublayer(borderLayer!)
- }
-
- if isAutoHiden {
- borderLayer?.opacity = 0
- let groupAnimation = CAAnimationGroup()
- groupAnimation.beginTime = CACurrentMediaTime()
- groupAnimation.duration = 1
- groupAnimation.fillMode = .forwards
- groupAnimation.isRemovedOnCompletion = true
- groupAnimation.repeatCount = 3
- let opacity = CABasicAnimation(keyPath: "opacity")
- opacity.fromValue = 0
- opacity.toValue = 1
- opacity.isRemovedOnCompletion = true
- let opacity2 = CABasicAnimation(keyPath: "opacity")
- opacity2.fromValue = 1
- opacity2.toValue = 0
- opacity2.isRemovedOnCompletion = false
- groupAnimation.animations = [opacity, opacity2]
- borderLayer?.add(groupAnimation, forKey: nil)
- }
- }
- // 设置画布比例
- public func resetCanvasFrame(frame: CGRect) {
- if self.frame.equalTo(frame) {
- FilterLog(2, message: "新老值一样,不重置")
- return
- }
- self.frame = frame
-
- mCanverSize = frame.size
- if isShowLine {
- showBorderLayer()
- }
- FilterLog(2, message: "new frame is \(frame)")
- renderView.isHidden = true
- renderView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
- renderView.resatSize()
- playerEmptyView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
- playMaskView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
- tipLab.frame = CGRect(x: (self.frame.size.width - 100) / 2, y: (self.frame.size.height - 14) / 2, width: 100, height: 14)
- progressLab.frame = CGRect(x: (self.frame.size.width - 140) / 2, y: 8, width: 140, height: 14)
- let bord = frame.size.width > frame.size.height ? CGFloat(60) : CGFloat(60)
- playView.frame = CGRect(x: (CGFloat(frame.size.width) - bord) / 2 , y: (CGFloat(frame.size.height) - bord) / 2, width: bord, height: bord)
-
- // 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)
- }
- override public func layoutSubviews() {
- super.layoutSubviews()
- }
- @objc func RenderViewOnclick() {
- if status == .playing {
- playView.isHidden = false
- playMaskView.isHidden = false
- pause()
- } else if status == .stop || status == .pause {
- playView.isHidden = true
- playMaskView.isHidden = true
- movie?.resume()
- speaker?.start()
- status = .playing
- }
- if renderViewOnClickHandle != nil {
- renderViewOnClickHandle!()
- }
- }
- func showPlayBtn(isHidden: Bool) {
- playView.isHidden = isHidden
- playMaskView.isHidden = isHidden
- }
- deinit {
- stop()
- movie = nil
- speaker = nil
- FilterLog(1, message: "play view release")
- }
- /// XXXX 这里的 URL 使用的是全路径 ,如果不是全的会 crash ,方便复用 (不用处理业务的文件放在哪里)
- 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) {
- self.isUsedAVPlayer = isUsedAVPlayer
- // 每次初始化的时候设置初始值 为 nIl
- var audioMix: AVMutableAudioMix?
- var composition: AVMutableComposition?
- let asset = AVURLAsset(url: url, options: nil)
- FilterLog(1, message: "播放器初始化的音频时长\(asset.duration.seconds) url is \(url),最终使用时长\(originMusicDuration),裁剪范围\(CMTimeGetSeconds(clipAudioRange.start)) 到 \(CMTimeGetSeconds(clipAudioRange.end))")
- self.asset = asset
- if (audioMixModel != nil && audioMixModel?.localPath != nil) || (videoStickers != nil && (videoStickers?.count ?? 0) > 0 || originMusicDuration != 0) {
- FilterLog(2, message: "有参加混音的数据。")
- (audioMix, composition) = PQPlayerViewModel.setupAudioMix(originAsset: asset, bgmData: audioMixModel, videoStickers: videoStickers,originMusicDuration:originMusicDuration,clipAudioRange: clipAudioRange)
- } else {
- audioMix = nil
- }
- isReadyToPlay = false
- asset.loadValuesAsynchronously(forKeys: ["tracks", "duration", "commonMetadata"]) { [weak self] in
- guard let strongSelf = self else { return }
- let tracksStatus = strongSelf.asset?.statusOfValue(forKey: AVAssetKey.tracks, error: nil) ?? .unknown
- let durationStatus = strongSelf.asset?.statusOfValue(forKey: AVAssetKey.duration, error: nil) ?? .unknown
- strongSelf.isReadyToPlay = tracksStatus == .loaded && durationStatus == .loaded
- }
- var audioSettings: [String: Any] = [
- AVFormatIDKey: kAudioFormatLinearPCM,
- ]
- // if #available(iOS 14.0, *) {
- audioSettings[AVLinearPCMIsFloatKey] = false
- audioSettings[AVLinearPCMBitDepthKey] = 16
- // }
- do {
- if composition != nil {
- FilterLog(2, message: "composition 方式初始化")
- movie = try PQMovieInput(asset: composition!, videoComposition: videoComposition, audioMix: audioMix, playAtActualSpeed: true, loop: isLoop, audioSettings: audioSettings)
- // movie?.exportAudioUrl = url // clipAudioRange
- var ranges = Array<CMTimeRange>()
- if CMTimeGetSeconds(clipAudioRange.duration) == 0 {
- let range = CMTimeRange(start: CMTime.zero, duration: asset.duration)
- ranges.append(range)
- }else{
- ranges.append(clipAudioRange)
- }
- movie?.configAVPlayer(assetUrl: url, ranges: ranges)
- } else {
- movie = try PQMovieInput(url: url, playAtActualSpeed: true, loop: isLoop, audioSettings: audioSettings)
- /* 测试代码
- let audioDecodeSettings = [AVFormatIDKey:kAudioFormatLinearPCM]
- let bundleURL = Bundle.main.resourceURL!
- let movieURL = URL(string:"11111.mp4", relativeTo:bundleURL)!
- movie = try MovieInput(url:movieURL, playAtActualSpeed:true, loop:true, audioSettings:audioDecodeSettings)
- */
- }
- movie!.runBenchmark = false
- movie!.synchronizedEncodingDebug = false
-
- movie!.isUsedAVPlayer = isUsedAVPlayer
- } catch {
- status = .error
- if enableDebug {
- debugPrint(error)
- }
- }
- guard let movie = movie else { return }
- movie.progress = { [weak self] currTime, duration, prgressValue in
- guard let strongSelf = self else { return }
- // FilterLog(1, message: " movie 进度\(currTime)")
- strongSelf.changeFilter(currTime: currTime)
- strongSelf.progress?(currTime, duration, prgressValue)
- DispatchQueue.main.async {
- strongSelf.playbackTime = currTime
- // Non-main thread change this property is not valid
- strongSelf.animationLayer?.timeOffset = strongSelf.playbackTime
- if strongSelf.showProgressLab {
- if(strongSelf.showProgressStartTime == 0 ){
- strongSelf.showProgressStartTime = Float(CMTimeGetSeconds(strongSelf.movie?.startTime ?? .zero))
- }
- if duration < 1 {
- strongSelf.progressLab.text = "\((currTime - Double(CMTimeGetSeconds(strongSelf.movie?.startTime ?? .zero))).formatDurationToHMS()) / 00:01"
- } else {
- var showTime = currTime - Double(strongSelf.showProgressStartTime)
- if (showTime < 0){
- showTime = 0
- }
- strongSelf.progressLab.text = "\(showTime.formatDurationToHMS()) / \( (duration - Double(strongSelf.showProgressStartTime)).formatDurationToHMS())"
- }
- }
- }
- }
- movie.completion = { [weak self] in
- guard let strongSelf = self else { return }
- //缓存已经用完,重新初始化缓存
- if(strongSelf.filters.count == 0){
- strongSelf.configCache(beginTime: strongSelf.mStickers?.first?.timelineIn ?? 0)
- }
-
- DispatchQueue.main.async {
- strongSelf.status = .stop
- strongSelf.finishedClosure?()
- strongSelf.showPlayBtn(isHidden: false)
- if(strongSelf.progress != nil){
- strongSelf.progress!(0,0,1)
- }
-
- }
- }
- speaker = SpeakerOutput()
- movie.audioEncodingTarget = speaker
- applyFilters()
- }
- /// 初始化缓存,默认选创建 cacheFiltersMaxCount 个缓存 filterrs
- /// - Parameter beginTime: 开始缓存的开始时间,用在 seek操作时 老的缓存已经无效不能在使用了
- func configCache(beginTime: Float64 ) {
- cacheFilters.removeAll()
- FilterLog(2, message: "原素材 总数:\(mStickers?.count ?? 0) ")
-
- if mStickers?.count ?? 0 > 0 {
- for (index, currentSticker) in mStickers!.enumerated() {
- FilterLog(message: "mStickers timelinein:\(currentSticker.timelineIn) timelineout: \(currentSticker.timelineOut) index : \(index)")
- //到达最大缓存数退出
- if cacheFilters.count == cacheFiltersMaxCount {
- break
- }
- //小于缓存的开始时间继续查找
- if(currentSticker.timelineOut < beginTime){
- continue
- }
- var showFitler: PQBaseFilter?
- if currentSticker.type == StickerType.VIDEO.rawValue {
- showFitler = PQMovieFilter(movieSticker: currentSticker)
- } else if currentSticker.type == StickerType.IMAGE.rawValue {
- showFitler = PQImageFilter(sticker: currentSticker, isExport: (movie?.mIsExport) ?? false, showUISize: mCanverSize)
- (showFitler as? PQImageFilter)?.isPointModel = ((mStickers?.count ?? 0) > 0)
- }
- if showFitler != nil {
- FilterLog(message: " 加入到缓存 的 filter timelinein:\(currentSticker.timelineIn) timelineout: \(currentSticker.timelineOut) in :\(currentSticker.model_in) out: \(currentSticker.out) index : \(index)")
- cacheFilters.append(showFitler!)
- }
- }
-
- DispatchQueue.global().async {[weak self] in
- if let strongSelf = self {
- for (index, filter) in strongSelf.cacheFilters.enumerated() {
- 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)")
- }
- }
- }
-
- if(cacheFilters.first != nil){
- movie?.removeAllTargets()
- let showFilter: PQBaseFilter = cacheFilters.first!
- movie?.addTarget(showFilter, atTargetIndex: 0)
- showFilter.addTarget(renderView, atTargetIndex: 0)
- }
-
- }
-
- }
- //创建下一个filter 数据
- func createNextFilter() {
- FilterLog(2, message: "加入前 当前的缓存个数为: \(cacheFilters.count) maxCount \(cacheFiltersMaxCount) 最后一个显示时间 \(String(describing: cacheFilters.last?.stickerInfo?.timelineIn))")
- if cacheFilters.count <= cacheFiltersMaxCount {
- let showIndex = mStickers?.firstIndex(where: { (sticker) -> Bool in
- (cacheFilters.last?.stickerInfo == sticker)
- })
- FilterLog(2, message: "当前显示的showIndex: \(String(describing: showIndex))")
- if ((showIndex ?? 0) + 1) < (mStickers?.count ?? 0) {
- let currentSticker = mStickers?[(showIndex ?? 0) + 1]
- if currentSticker != nil {
- var showFitler: PQBaseFilter?
- if currentSticker!.type == StickerType.VIDEO.rawValue {
- showFitler = PQMovieFilter(movieSticker: currentSticker!)
- } else if currentSticker!.type == StickerType.IMAGE.rawValue {
- showFitler = PQImageFilter(sticker: currentSticker!, isExport: (movie?.mIsExport) ?? false, showUISize: mCanverSize)
- (showFitler as? PQImageFilter)?.isPointModel = ((mStickers?.count ?? 0) > 0)
- }
- if showFitler != nil {
- cacheFilters.append(showFitler!)
- }
- }else{
- FilterLog(2, message: "缓存数据加入不成功!!!!!")
- }
- }
-
- FilterLog(2, message: "加入后 当前的缓存个数为: \(cacheFilters.count) maxCount \(cacheFiltersMaxCount) 最后一个显示时间 \(String(describing: cacheFilters.last?.stickerInfo?.timelineIn))")
-
- }
-
-
- }
-
-
- /// 按时间从缓存中取出要显示的filter
- /// - Parameter currTime: 当前播放时间
- func changeFilter(currTime: Float64) {
- // let starts:CFTimeInterval = CFAbsoluteTimeGetCurrent()
- FilterLog(message: " 要查找的 currTime is \(currTime)")
- //1,删除已经显示过的 filter
- self.cacheFilters.removeAll(where: {(filter) -> Bool in
- (currTime > (filter.stickerInfo?.timelineOut ?? 0.0))
- })
-
- // 2,找出一个要显示的 fitler
- let showIndex = cacheFilters.firstIndex(where: { (filter) -> Bool in
- (currTime >= (filter.stickerInfo?.timelineIn ?? 0.0) && currTime <= (filter.stickerInfo?.timelineOut ?? 0.0))
- })
- if(showIndex == nil){
- FilterLog(2, message: "缓存没有查找到?出现数据错误!!!!")
- return
- }
-
- let showFilter: PQBaseFilter = cacheFilters[showIndex ?? 0]
-
- 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))")
-
- if(!(showFilter.isShow)){
- 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))")
-
- showFilter.isShow = true
-
- movie!.removeAllTargets()
-
- //为了优化性能只有素材宽高比和画面宽高比不一样时才做高斯
- //原图的比例
- let stickerAspectRatio = String(format: "%.6f", (showFilter.stickerInfo?.width ?? 0.0 ) / (showFilter.stickerInfo?.height ?? 0.0))
- //画面的比例
- let canverAspectRatio = String(format: "%.6f",(movie?.mShowVidoSize.width ?? 0.0) / (movie?.mShowVidoSize.height ?? 0.0))
- if(showFilter.stickerInfo?.type == StickerType.IMAGE.rawValue && showGaussianBlur && Float(stickerAspectRatio) != Float(canverAspectRatio)){
- FilterLog(2, message: "显示图片filter")
- // //高斯层
- let blurStickerModel:PQEditVisionTrackMaterialsModel? = showFilter.stickerInfo?.copy() as? PQEditVisionTrackMaterialsModel
- blurStickerModel?.canvasFillType = stickerContentMode.aspectFillStr.rawValue
- if blurStickerModel == nil {
- FilterLog(2, message: "显示图片filter blurStickerModel is nil")
- return
- }
- let showGaussianFitler:PQBaseFilter = PQImageFilter(sticker: blurStickerModel!, isExport: (movie?.mIsExport) ?? false, showUISize: mCanverSize)
- (showGaussianFitler as? PQImageFilter)?.isPointModel = ((mStickers?.count ?? 0) > 0)
-
- let iosb:GaussianBlur = GaussianBlur.init()
- iosb.blurRadiusInPixels = 20
- showGaussianFitler.addTarget(iosb)
-
- self.movie?.addTarget(showGaussianFitler, atTargetIndex: 0)
- iosb.addTarget(showFilter,atTargetIndex: 0)
- showFilter.addTarget(self.renderView as ImageConsumer, atTargetIndex: 0)
-
- FilterLog(2, message: "filter 添加成功 注意是否添加成功。")
-
- // }
-
- }else{
- movie?.addTarget(showFilter, atTargetIndex: 0)
- showFilter.addTarget(renderView, atTargetIndex: 0)
- }
- self.createFiltersQueue.async {
- self.createNextFilter()
- }
- }else{
- FilterLog(2, message: " 添加过了 currTime is \(currTime) timelineIn:\(showFilter.stickerInfo?.timelineIn ?? 0.0)")
- }
- }
- /// 设置 filter 是否为 seek 状态
- func setEnableSeek(isSeek: Bool) {
- for filter in filters {
- (filter as? PQBaseFilter)?.enableSeek = isSeek
- }
- }
- private func applyFilters() {
- guard let movie = movie else { return }
- movie.removeAllTargets()
- var currentTarget: ImageSource = movie
- filters.forEach {
- let f = $0
- currentTarget.addTarget(f, atTargetIndex: 0)
- currentTarget = f
- }
- currentTarget.addTarget(renderView, atTargetIndex: 0)
- }
- }
- // MARK: Player control
- public extension PQGPUImagePlayerView {
- /// 开始播放
- /// - Parameter pauseFirstFrame: 是否暂停到第一帧
- func play(pauseFirstFrame: Bool = false, playeTimeRange: CMTimeRange = CMTimeRange()) {
- DispatchQueue.main.async {
- self.playerEmptyView.isHidden = true
- self.playView.isHidden = !pauseFirstFrame
- self.playMaskView.isHidden = !pauseFirstFrame
- self.renderView.isHidden = false
- self.progressLab.isHidden = false
- }
- // guard status != .playing else {
- // FilterLog(2, message: "已经是播放状态")
- // return
- // }
- // 如果没有设置开始结束时长 使用默认音频总时长(创作工具就不会传值)
- if CMTIMERANGE_IS_INVALID(playeTimeRange) {
- let endTime = CMTime(value: CMTimeValue(CMTimeGetSeconds(asset?.duration ?? .zero) * 600), timescale: 600)
- mPlayeTimeRange = CMTimeRange(start: .zero, end: endTime)
- } else {
- mPlayeTimeRange = playeTimeRange
- }
- // 清空音频缓存
- speaker?.clearBuffer()
- movie?.start(timeRange: mPlayeTimeRange ?? CMTimeRange())
- speaker?.start()
- status = pauseFirstFrame ? .pause : .playing
-
- showProgressStartTime = 0
- }
- // 快进
- func seek(to time: CMTime) {
- mPlayeTimeRange?.start = time
- play(pauseFirstFrame: false, playeTimeRange: mPlayeTimeRange ?? .zero)
- }
- // 暂停
- func pause() {
- guard status != .pause else {
- return
- }
- movie?.pause() // 可能会引起crash: configureThread()里timebaseInfo为0,除法出错
- speaker?.pause()
- status = .pause
- showPlayBtn(isHidden: false)
- }
- // 停止f解码状态
- func stop() {
- // guard status != .stop else {
- // return
- // }
- movie?.removeAllTargets()
- movie?.cancel()
- speaker?.cancel()
- status = .stop
- }
- // 清空播放器状态,到空状态
- func clearPlayerView() {
- playerEmptyView.isHidden = false
- renderView.isHidden = true
- progressLab.isHidden = true
- }
- // 显示提示文字
- func showTip(show: Bool) {
- FilterLog(2, message: "showTip \(show)")
- tipLab.isHidden = !show
- if show {
- playerEmptyView.isHidden = true
- renderView.isHidden = true
- progressLab.isHidden = true
- }
- }
- }
- // MARK: Filter 操作
- public extension PQGPUImagePlayerView {
- // 添加 filter
- func appendFilter(_ filter: ImageProcessingOperation) {
- filters.append(filter)
- }
- // 添加一组filters
- func appendFilters(_ newFilters: [ImageProcessingOperation]) {
- filters = filters + newFilters
- }
- // 移除所有filter
- func removeAllFilters() {
- filters.removeAll()
- }
- // 重置所有 filer
- func appendFiltersClearOldFilter(_ newFilters: [ImageProcessingOperation]) {
- filters.removeAll()
- filters = newFilters
- }
- }
- // MARK: - RenderViewDelegate
- extension PQGPUImagePlayerView: RenderViewDelegate{
- public func willDisplayFramebuffer(renderView _: RenderView, framebuffer _: Framebuffer) {
- FilterLog(2, message: "willDisplayFramebuffer")
- }
- public func didDisplayFramebuffer(renderView _: RenderView, framebuffer: Framebuffer) {
- FilterLog(2, message: "didDisplayFramebuffer")
- }
- public func shouldDisplayNextFramebufferAfterMainThreadLoop() -> Bool {
- FilterLog(2, message: "didDisplayFramebuffer")
-
- return false
- }
- }
|