//
//  PQMoveInput.swift
//  GPUImage_macOS
//
//  Created by ak on 2020/8/16.
//  Copyright © 2020 Sunset Lake Software LLC. All rights reserved.
//  从 GPUIMGE2 moveinput 修改版本 使用音频驱动

import AVFoundation
import VideoToolbox
import BFCommonKit

public protocol PQMovieInputDelegate: AnyObject {

}

public class PQMovieInput: ImageSource {
    public func transmitPreviousImage(to _: ImageConsumer, atIndex _: UInt) {}

    public let targets = TargetContainer()
    public var runBenchmark = false
    
    var avPlayer:AVPlayer?
    var indexRage = 0
    var orginStuckRange : CMTimeRange?{
        didSet{
            if orginStuckRange != nil {
                self.musicPlayRanges = [orginStuckRange!]
            }
        }
    }
    // 应存储多个 播放片断的时间座标，目前就只需要存放音乐本身的卡点座标就可以
    var musicPlayRanges:[CMTimeRange]?
    
    public weak var delegate: PQMovieInputDelegate?

    public var audioEncodingTarget: AudioEncodingTarget? {
        didSet {
            guard let audioEncodingTarget = audioEncodingTarget else {
                return
            }
            audioEncodingTarget.activateAudioTrack()

            // Call enableSynchronizedEncoding() again if they didn't set the audioEncodingTarget before setting synchronizedMovieOutput.
            if synchronizedMovieOutput != nil { enableSynchronizedEncoding() }
        }
    }

    public  let yuvConversionShader: ShaderProgram
    public  let asset: AVAsset
    public   let videoComposition: AVVideoComposition?
    public  var playAtActualSpeed: Bool

    // Time in the video where it should start.
//    var requestedStartTime: CMTime?
    //播放器初始播放的开始和结束时间
    public  var playeTimeRange:CMTimeRange = CMTimeRange.init(start: .zero, duration: .positiveInfinity)
    
    // Time in the video where it started.
    public  var startTime: CMTime?
    // Time according to device clock when the video started.
    public  var actualStartTime: DispatchTime?
    // Last sample time that played. 当前的帧的时间戳，两个驱动方式取值方法不一样
    public private(set) var currentTime: CMTime = .zero

    public var loop: Bool

    // Called after the video finishes. Not called when cancel() or pause() is called.
    public var completion: (() -> Void)?
    // Progress block of the video with a paramater value of 0-1.
    // Can be used to check video encoding progress. Not called from main thread.
    // 参数说明：1,当前时间 2，总时长 3，进度
    public var progress: ((Double, Double, Double) -> Void)?

    public var synchronizedMovieOutput: MovieOutput? {
        didSet {
            enableSynchronizedEncoding()
        }
    }

    public var synchronizedEncodingDebug = false {
        didSet {
            synchronizedMovieOutput?.synchronizedEncodingDebug = synchronizedEncodingDebug
        }
    }

    public  let conditionLock = NSCondition()
    public  var readingShouldWait = false
    public   var videoInputStatusObserver: NSKeyValueObservation?
    public var audioInputStatusObserver: NSKeyValueObservation?
    var avPlayerTimeObserver: NSKeyValueObservation?
    
    public var useRealtimeThreads = false
    public  var timebaseInfo = mach_timebase_info_data_t()

    public  var totalFramesSent = 0
    public  var totalFrameTimeDuringCapture: Double = 0.0

    public  var audioSettings: [String: Any]?

    public  var movieFramebuffer: Framebuffer?
    // 播放状态
    public var isPlay: Bool = false

    // 时间驱动对象
    public  var displayLink: CADisplayLink?

    // 时间驱动时的开始时间，保存currentTime 从0开始
    public  var beginTime: CFTimeInterval = 0

    public  let audioMix: AVAudioMix?

    // 图片纹理
    public  var imageTexture: GLuint = 0

    public  var imageFramebuffer: Framebuffer!

    public  var readerVideoTrackOutput: AVAssetReaderOutput?
    public  var readerAudioTrackOutput: AVAssetReaderOutput?

    public  var assetReader: AVAssetReader!
 

    public  var mFreeBuffer: Bool = false

    // 是否自动暂停 要定到首帧,不是才播放出声音
    public  var mAutoPause: Bool = false

    /// Use serial queue to ensure that the picture is smooth
//    public  var seekQueue: DispatchQueue!

    // 是否为导出模式
    public   var mIsExport: Bool = false
    
    //打印开始时间
    var debugStartTime:CFTimeInterval?

     // 画布的大小 注意要是偶数 要不在 IOS 13上会有绿边 自动放大到偶数
    public var mShowVidoSize: CGSize = cVideoCannvasSizeOneToOne {
        didSet {
            BFLog(2, message: "mShowVidoSize is move input  \(mShowVidoSize)")
            do {
                displayLink?.isPaused = true
                imageFramebuffer = try Framebuffer(context: sharedImageProcessingContext, orientation: .portrait, size: GLSize(width: GLint(mShowVidoSize.width), height: GLint(mShowVidoSize.height)), textureOnly: true)
                displayLink?.isPaused = false
                //            imageFramebuffer.texture = imageTexture
            } catch {
                fatalError("Could not create a framebuffer  error: \(error)")
            }
        }
    }
    // 初始化方法
    public init(asset: AVAsset, videoComposition: AVVideoComposition?, audioMix: AVAudioMix?, playAtActualSpeed: Bool = false, loop: Bool = false, audioSettings: [String: Any]? = nil) throws {
        self.asset = asset
        BFLog(2, message: "asset 资源的总时长\(asset.duration.seconds) \(asset.duration)")
        self.audioMix = audioMix
        self.audioSettings = audioSettings
        self.videoComposition = videoComposition
        self.playAtActualSpeed = playAtActualSpeed
        self.loop = loop
        yuvConversionShader = crashOnShaderCompileFailure("MovieInput") { try sharedImageProcessingContext.programForVertexShader(defaultVertexShaderForInputs(2), fragmentShader: YUVConversionFullRangeFragmentShader) }
        if asset.duration.seconds <= 0 {
            BFLog(2, message: "asset 资源的总时长为0，返回")
            return
        }
  
        // XXXX   并不是使用 CADisplayLink 的时间 那个时间是刷新时间有可能会跳帧
        displayLink = CADisplayLink(target: self, selector: #selector(displayLinkClick(_:)))
        // 设置触发频率 这个周期可以通过frameInterval属性设置，CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2，每秒调用就变成30次

        if #available(iOS 10, *){
            displayLink?.preferredFramesPerSecond = 30
//            seekQueue = DispatchQueue(label: "PQ.MovieInput.seeking", qos: .userInteractive, attributes: .initiallyInactive, autoreleaseFrequency: .never, target: nil)
        }else{
            displayLink?.frameInterval = 2
//            seekQueue = DispatchQueue(label: "PQ.MovieInput.seeking", qos: .userInteractive, attributes: [], autoreleaseFrequency: .inherit, target: nil)
        }
        // 加入循环 要使用 common 不要让级别高的卡住回调事件
        displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
        displayLink?.isPaused = true
    }

    // 初始化方法
    public convenience init(url: URL, playAtActualSpeed: Bool = false, loop: Bool = false, audioSettings: [String: Any]? = nil) throws {
        let inputAsset = AVURLAsset(url: url, options: nil)
        try self.init(asset: inputAsset, videoComposition: nil, audioMix: nil, playAtActualSpeed: playAtActualSpeed, loop: loop, audioSettings: audioSettings)
    }

    // 析构方法
    deinit {
        self.movieFramebuffer?.unlock()
        self.cancel()

        PQNotification.removeObserver(avPlayer?.currentItem as Any)
        avPlayer?.replaceCurrentItem(with: nil)
        self.videoInputStatusObserver?.invalidate()
        self.audioInputStatusObserver?.invalidate()
        self.avPlayerTimeObserver?.invalidate()

        BFLog(1, message: "movieinput release")
    }

    // MARK: -

    // MARK: Playback control

    // 开始
    public func start(timeRange:CMTimeRange) {
        isPlay = false
        beginTime = 0
        currentTime = .zero
        BFLog(2, message: "初始化播放开始时间、\(CMTimeGetSeconds(timeRange.start)) 结束时间\(CMTimeGetSeconds(timeRange.end)) 播放总时长:\(CMTimeGetSeconds(timeRange.end) - CMTimeGetSeconds(timeRange.start))")
  
        playeTimeRange = timeRange
        startTime = playeTimeRange.start

        start(isFreeBuffer: false,isExport: false,timeRange: playeTimeRange)
    }

    @objc func displayLinkClick(_ displayLink: CADisplayLink) {
        if assetReader == nil {
            BFLog(2, message: "self.assetReader is null !!!!!!!!!!")
            displayLink.isPaused = true
            return
        }
        
        // 预览模式
        if !mIsExport {
            if self.beginTime == 0 {
                self.beginTime = CFAbsoluteTimeGetCurrent()
            }
        }

 
        autoreleasepool {
      
            let duration = CMTimeGetSeconds(playeTimeRange.end)
            
            if !mIsExport {
                self.currentTime = CMTimeMakeWithSeconds(CFAbsoluteTimeGetCurrent() - self.beginTime +  CMTimeGetSeconds(startTime ?? CMTime.zero), preferredTimescale: BASE_FILTER_TIMESCALE)
            }else {
                self.currentTime = CMTimeMakeWithSeconds(self.beginTime +  CMTimeGetSeconds(startTime ?? CMTime.zero), preferredTimescale: BASE_FILTER_TIMESCALE)
                self.beginTime = self.beginTime + 1 / 30
            }
            
            let currTime = CMTimeGetSeconds(self.currentTime)
  
            let prgressValue = currTime / Float64(duration)
            
            BFLog(2, message: "\(mIsExport) MovieOutput total frames appended:播放进行中 总用时: \(CFAbsoluteTimeGetCurrent() - (debugStartTime ?? 0.0)) 播放器开始时间:\(CMTimeGetSeconds(playeTimeRange.start)) 播放器原始结束时间:\(CMTimeGetSeconds(playeTimeRange.end))    总时间：\(CMTimeGetSeconds(playeTimeRange.end) - CMTimeGetSeconds(playeTimeRange.start)) 播放进度当前时间：\(currTime) 进度:\(prgressValue) 帧id \(totalFramesSent)")
            totalFramesSent += 1
    
            BFLog(2, message: "2222222222播放进行中 总用时: \(CFAbsoluteTimeGetCurrent() - (debugStartTime ?? 0.0)) 播放进度当前时间：\(currTime) 总时间为：\(duration)进度:\(prgressValue) 音频时长：\(    CMTimeGetSeconds(asset.duration) )")
            if currTime / duration > 1{
                BFLog(2, message: "全部播放完成 总用时为：\(CFAbsoluteTimeGetCurrent() - (debugStartTime ?? 0.0))")
                if(self.completion != nil){
                    self.completion?()
                }
               
                self.beginTime = 0
                self.currentTime =  CMTimeMakeWithSeconds(CMTimeGetSeconds(playeTimeRange.start), preferredTimescale: BASE_FILTER_TIMESCALE)
                self.isPlay = false
                self.mAutoPause = true

                // 定到首帧，最好移动到业务逻辑中。。。TODO不是导出模式才定帧
                if !mIsExport {
                    self.start(isFreeBuffer: true,timeRange: playeTimeRange)
                }else{
                    BFLog(message: "强制停止！！！！")
                    displayLink.isPaused = true
                    return
                }
            }

            if let movieOutput = self.synchronizedMovieOutput {
                self.conditionLock.lock()
                while self.readingShouldWait {
                    self.synchronizedEncodingDebugPrint("Disable reading")
                    BFLog(2, message: "Disable reading 开始等待 \(CFAbsoluteTimeGetCurrent() - (debugStartTime ?? 0.0)) ")
                    self.conditionLock.wait()
                    BFLog(2, message: "Enable reading  停止等待 \(CFAbsoluteTimeGetCurrent() - (debugStartTime ?? 0.0))")
                }
                self.conditionLock.unlock()

                if movieOutput.assetWriterVideoInput.isReadyForMoreMediaData {
                    self.readNextVideoFrame(with: self.assetReader)
                }
                if movieOutput.assetWriterAudioInput?.isReadyForMoreMediaData ?? false {
                    if let readerAudioTrackOutput = self.readerAudioTrackOutput {
                        self.readNextAudioSample(with: self.assetReader, from: readerAudioTrackOutput)
                    }
                }
            } else {
                self.readNextVideoFrame(with: self.assetReader)
                if let readerAudioTrackOutput = self.readerAudioTrackOutput,
                   self.audioEncodingTarget?.readyForNextAudioBuffer() ?? true
                {
                    self.readNextAudioSample(with: self.assetReader, from: readerAudioTrackOutput)
                }
            }
            // 如果自动循环 播放完重新启动
            if self.loop, displayLink.isPaused {
                self.start(isFreeBuffer: false)
            }
            
          
        }
    }

    // 开始 isFreeBuffer 是否是定首帧功能，定首帧就不处理声音了 会播放出来
    @objc public func start(isFreeBuffer: Bool, isExport: Bool = false,timeRange:CMTimeRange = CMTimeRange.init()) {
        
        debugStartTime = CFAbsoluteTimeGetCurrent()
        BFLog(2, message: "开始播放的系统时钟时间 \(String(describing: debugStartTime))")
        
        playeTimeRange = timeRange
        readerAudioTrackOutput = nil
        BFLog(2, message: "PQMoveInput开始")
        mFreeBuffer = isFreeBuffer
        assetReader = createReader()
    
        startTime = playeTimeRange.start

        isPlay = true
        if assetReader == nil {
            BFLog(2, message: "assetReader is null!!!!!")
            return
        }

        mIsExport = isExport
        do {
            try NSObject.catchException { [self] in
                if(mIsExport){
                    guard self.assetReader.startReading() else {
                        BFLog(2, message: "ERROR: Unable to start reading: \(String(describing: self.assetReader.error))")
                        return
                    }
                }else{
                    if !mAutoPause {
                        avplayerMusic()
                    }
                }
             
              
            }
        } catch {
            BFLog(2, message: "ERROR: Unable to start reading: \(error)")
            return
        }

        BFLog(2, message: "assetReader.outputs count is \(assetReader.outputs)")
        for output in assetReader.outputs {
            if output.mediaType == AVMediaType.video {
                readerVideoTrackOutput = output
                BFLog(2, message: " set  readerVideoTrackOutput")
            }
            if output.mediaType == AVMediaType.audio {
                readerAudioTrackOutput = output
                BFLog(2, message: " set  readerAudioTrackOutput")
            }
        }

        isPlay = true
        if isExport {
            while isPlay {
                if displayLink != nil {
                    displayLinkClick(displayLink!)
                }
            }
        } else {
            displayLink?.isPaused = false
        }
    }

    public func cancel() {
        BFLog(2, message: "PQMoveInput取消")
        isPlay = false

        // 将定时器移除主循环
        displayLink?.remove(from: RunLoop.main, forMode: RunLoop.Mode.default)
        // 停止定时器
        displayLink?.invalidate()
        displayLink = nil

        beginTime = 0
        
        avPlayer?.pause()
    }

    public func resume() {
        BFLog(2, message: "PQMoveInput恢复播放")
        mFreeBuffer = false
        isPlay = true
        if !mIsExport{
            self.beginTime = CFAbsoluteTimeGetCurrent() - CMTimeGetSeconds(self.currentTime) + CMTimeGetSeconds(self.startTime ?? CMTime(value: 0, timescale: 1))
        }

        // 暂停帧的刷新 true:停 ; false:开始
        displayLink?.isPaused = false
        avPlayer?.play()
       
    }

    public func pause() {
        if !isPlay {
            BFLog(2, message: "还不是播放状态")
            return
        }

        if displayLink == nil {
            BFLog(2, message: "displayLink is erorr displaye bug !!!!")
            return
        }
        BFLog(2, message: "PQMoveInput暂停 displayLink.timestamp:  \(displayLink!.timestamp)")
        isPlay = false

        // 暂停帧的刷新 true:停 ; false:开始
        displayLink?.isPaused = true

        // 暂停时当前播放时间 <  总长时说明没有播放完，设置下一次play 的开始时间
        let currTime = CMTimeGetSeconds(currentTime)
       
        if Double(currTime) <  CMTimeGetSeconds(playeTimeRange.end) {
            //记录暂停时间，重新播放从这个时间开始
//            playeTimeRange.start =
            
            startTime = currentTime
        }

        BFLog(2, message: "暂停时间:\(currTime)")

        beginTime = 0
        
        avPlayer?.pause()
    }
    

    // MARK: -

    // MARK: Internal processing functions

    // 创建 reader
    func createReader() -> AVAssetReader? {
        do {
            let assetReader = try AVAssetReader(asset: asset)
            BFLog(2, message: "assetReader init \(assetReader)  asset url is \(asset)")
            if audioMix == nil {
                if let audioTrack = asset.tracks(withMediaType: .audio).first, let _ = audioEncodingTarget {
                    BFLog(1, message: "audioTrack start \(audioTrack.timeRange.start) \(audioTrack.timeRange.duration.value)")

                    let readerAudioTrackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: audioSettings)
                    readerAudioTrackOutput.alwaysCopiesSampleData = false
                    // 不按照顺序读取 是否支持重新设置数据的读取时间范围，即能否修改 reader 的 timeRange 属性
                    // readerVideoTrackOutput.supportsRandomAccess = true
                    assetReader.add(readerAudioTrackOutput)
                    
                }

            } else {
                BFLog(2, message: "self.asset.tracks is \(asset.tracks.count)")
                let readerAudioTrackOutput = AVAssetReaderAudioMixOutput(audioTracks: asset.tracks(withMediaType: .audio), audioSettings: audioSettings)
                readerAudioTrackOutput.audioMix = audioMix
                readerAudioTrackOutput.alwaysCopiesSampleData = false
                assetReader.add(readerAudioTrackOutput)
            }

            assetReader.timeRange = playeTimeRange
            BFLog(2, message: "初始化播放器开始时间\(CMTimeGetSeconds(assetReader.timeRange.start)) 结束时间\(CMTimeGetSeconds(assetReader.timeRange.end)) 音乐的总时长\(asset.duration.seconds)")
       

            actualStartTime = nil

            return assetReader
        } catch {
            BFLog(2, message: "ERROR: Unable to create asset reader: \(error)")
        }
        return nil
    }

    func readNextVideoFrame(with _: AVAssetReader) {
        
        BFLog(2, message: "视频解码状态\(assetReader.status.rawValue)")
     
        autoreleasepool {
            synchronizedEncodingDebugPrint("Process frame input")
            
            let duration =  CMTimeGetSeconds(playeTimeRange.end)
         
            let start =  CMTimeGetSeconds(playeTimeRange.start)
            let currTime = CMTimeGetSeconds(currentTime)
            
    
            let prgressValue = (currTime - start) / (duration - start)
//            BFLog(1, message: "\(mIsExport) movinput 当前时间 is \(currTime) curr当前进度：\(prgressValue)")
            progress?(currTime, duration, prgressValue)

            sharedImageProcessingContext.runOperationSynchronously { [weak self] in
                self?.process()
            }
        }
    }

    func readNextAudioSample(with assetReader: AVAssetReader, from audioTrackOutput: AVAssetReaderOutput) {
        
        if(!mIsExport){
            BFLog(2, message: "不是导出模式")
            return
        }
       
        if !isPlay {
            BFLog(2, message: "自动停到首帧的不处理音频")
            return
        }
        /*
         case reading = 1
         case completed = 2
         case failed = 3
         case cancelled = 4
         */
        BFLog(2, message: "音频解码状态\(assetReader.status.rawValue)")
        
        autoreleasepool {
            guard let sampleBuffer = audioTrackOutput.copyNextSampleBuffer(),CMSampleBufferIsValid(sampleBuffer) else {
                if(assetReader.status == .completed){
                    BFLog(2, message: "提前结束的了！！！！\(String(describing: assetReader.error))")
                }
                if let movieOutput = synchronizedMovieOutput {
                    BFLog(2, message: "this is runing assetWriterAudioInput  markAsFinished \(String(describing: assetReader.error)) \(assetReader)")

                    movieOutput.movieProcessingContext.runOperationAsynchronously {
                        movieOutput.audioEncodingIsFinished = true
                        movieOutput.assetWriterAudioInput?.markAsFinished()
                    }
                }
                BFLog(2, message: "sampleBuffer is null 速度太快copy is error")
                return
            }

            synchronizedEncodingDebugPrint("Process audio sample input")

            let currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer)
            BFLog(2, message: "处理音频的时间戳 \(CMTimeGetSeconds(currentSampleTime)) 播放时间\(CMTimeGetSeconds(currentTime))")

            // https://www.itdaan.com/blog/2013/11/28/deb10f90970a5ea33f185c9faf2a0ab3.html
            if !mFreeBuffer {
                audioEncodingTarget?.processAudioBuffer(sampleBuffer)
            }else{
                BFLog(message: "不播放音频！！！！")
            }
        }
    }

    func process() {
        autoreleasepool {
            let startPTime = CFAbsoluteTimeGetCurrent()

            imageFramebuffer.lock()
            BFLog(message: "mIsExport:\(mIsExport) 实际设置的每一帧时间戳:\(CMTimeGetSeconds(currentTime))")
            // 设置当前帧的时间戳
            imageFramebuffer.timingStyle = .videoFrame(timestamp: Timestamp(currentTime))
 
            updateTargetsWithFramebuffer(imageFramebuffer)

            imageFramebuffer.unlock()

            if runBenchmark || synchronizedEncodingDebug {
                totalFramesSent += 1
            }

            if runBenchmark {
                let currentFrameTime = (CFAbsoluteTimeGetCurrent() - startPTime)
                totalFrameTimeDuringCapture += currentFrameTime
                
                
                BFLog(2, message: "currentTime is \(String(format: "%.6f", CMTimeGetSeconds(currentTime))) 当前帧渲染时间 : \(String(format: "%.6f",1000.0 * currentFrameTime)) ms Average frame time : \(String(format: "%.6f", 1000.0 * totalFrameTimeDuringCapture / Double(totalFramesSent))) ms  totalFrameTimeDuringCapture is \(String(format: "%.6f",totalFrameTimeDuringCapture))")
             
            

            }

            if mAutoPause {
                mAutoPause = false
                pause()
                avPlayerPlayComplete()
            }
        }
    }

    // MARK: -

    // MARK: Synchronized encoding

    func enableSynchronizedEncoding() {
        synchronizedMovieOutput?.encodingLiveVideo = false
        synchronizedMovieOutput?.synchronizedEncodingDebug = synchronizedEncodingDebug
        playAtActualSpeed = false
        loop = false

        // Subscribe to isReadyForMoreMediaData changes
        setupObservers()
        // Set the intial state of the lock
        updateLock()
    }

    func setupObservers() {
        videoInputStatusObserver?.invalidate()
        audioInputStatusObserver?.invalidate()

        guard let movieOutput = synchronizedMovieOutput else { return }

        videoInputStatusObserver = movieOutput.assetWriterVideoInput.observe(\.isReadyForMoreMediaData, options: [.new, .old]) { [weak self] _, _ in
            guard let weakSelf = self else { return }
            weakSelf.updateLock()
        }
        audioInputStatusObserver = movieOutput.assetWriterAudioInput?.observe(\.isReadyForMoreMediaData, options: [.new, .old]) { [weak self] _, _ in
            guard let weakSelf = self else { return }
            weakSelf.updateLock()
        }
    }

    func updateLock() {
        guard let movieOutput = synchronizedMovieOutput else { return }

        conditionLock.lock()
        // Allow reading if either input is able to accept data, prevent reading if both inputs are unable to accept data.
        if movieOutput.assetWriterVideoInput.isReadyForMoreMediaData || movieOutput.assetWriterAudioInput?.isReadyForMoreMediaData ?? false {
            readingShouldWait = false
            conditionLock.signal()
            
        } else {
            BFLog(1, message: "MovieOutput total frames appended 要加锁了")
            readingShouldWait = true
        }
        conditionLock.unlock()
    }

    // MARK: -

    // MARK: Thread configuration

    func configureThread() {
        let clock2abs = Double(timebaseInfo.denom) / Double(timebaseInfo.numer) * Double(NSEC_PER_MSEC)

        // http://docs.huihoo.com/darwin/kernel-programming-guide/scheduler/chapter_8_section_4.html
        //
        // To see the impact of adjusting these values, uncomment the print statement above mach_wait_until() in self.readNextVideoFrame()
        //
        // Setup for 5 ms of work.
        // The anticpated frame render duration is in the 1-3 ms range on an iPhone 6 for 1080p without filters and 1-7 ms range with filters
        // If the render duration is allowed to exceed 16ms (the duration of a frame in 60fps video)
        // the 60fps video will no longer be playing in real time.
        let computation = UInt32(5 * clock2abs)
        // Tell the scheduler the next 20 ms of work needs to be done as soon as possible.
        let period = UInt32(0 * clock2abs)
        // According to the above scheduling chapter this constraint only appears relevant
        // if preemtible is set to true and the period is not 0. If this is wrong, please let me know.
        let constraint = UInt32(5 * clock2abs)

        // print("period: \(period) computation: \(computation) constraint: \(constraint)")

        let THREAD_TIME_CONSTRAINT_POLICY_COUNT = mach_msg_type_number_t(MemoryLayout<thread_time_constraint_policy>.size / MemoryLayout<integer_t>.size)

        var policy = thread_time_constraint_policy()
        var ret: Int32
        let thread: thread_port_t = pthread_mach_thread_np(pthread_self())

        policy.period = period
        policy.computation = computation
        policy.constraint = constraint
        policy.preemptible = 0

        ret = withUnsafeMutablePointer(to: &policy) {
            $0.withMemoryRebound(to: integer_t.self, capacity: Int(THREAD_TIME_CONSTRAINT_POLICY_COUNT)) {
                thread_policy_set(thread, UInt32(THREAD_TIME_CONSTRAINT_POLICY), $0, THREAD_TIME_CONSTRAINT_POLICY_COUNT)
            }
        }

        if ret != KERN_SUCCESS {
            mach_error("thread_policy_set:", ret)
            print("Unable to configure thread")
        }
    }

    func nanosToAbs(_ nanos: UInt64) -> UInt64 {
        return nanos * UInt64(timebaseInfo.denom) / UInt64(timebaseInfo.numer)
    }

    func synchronizedEncodingDebugPrint(_ string: String) {
        if synchronizedMovieOutput != nil, synchronizedEncodingDebug { print(string) }
    }
}

//var indexRage = 0

extension PQMovieInput {
    
    /*
     ranges: 考虑有多种播放范围拼接的情况下，播放范围的数组。如果是当前版本的需求（2021-09-17），只有一种播放范围，则可以设置loopCount为大数（例如1000），保证无限循环，由视频的播放回调progress()确定播放完毕，调用avPlayerPlayComplete()来停止音乐的播放，并重置音乐播放器
     */
    
    func configAVPlayer(assetUrl:URL, ranges:[CMTimeRange]) {
        self.musicPlayRanges = ranges
        let playerItem = AVPlayerItem(url: assetUrl)
        avPlayer = AVPlayer.init(playerItem: playerItem)
//        let range = ranges.first
        // 当前版本设置无限大
        let loopCount = 10000 // ranges.count

        PQNotification.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer?.currentItem, queue: .main) { [weak self] notify in
            guard let strongSelf = self else {
                return
            }
            if strongSelf.avPlayerTimeObserver != nil {
                strongSelf.avPlayer!.removeTimeObserver(strongSelf.avPlayerTimeObserver as Any)
            }
//            strongSelf.indexRage += 1

            if strongSelf.indexRage < loopCount && strongSelf.isPlay && CMTimeGetSeconds(strongSelf.playeTimeRange.end) > CMTimeGetSeconds((strongSelf.musicPlayRanges?.first!.end)!) {
                
                let range = strongSelf.musicPlayRanges?[strongSelf.indexRage]
                playerItem.forwardPlaybackEndTime = range!.end
                playerItem.reversePlaybackEndTime = range!.start
//                BFLog(1, message: "curr: start ********************\(CMTimeGetSeconds(range.start)) - \(playerItem.reversePlaybackEndTime) - \(playerItem.forwardPlaybackEndTime)")
                strongSelf.avPlayer!.seek(to: playerItem.reversePlaybackEndTime) { isSuccess in
                    playerItem.seek(to: playerItem.reversePlaybackEndTime) { isSuccess in
                        strongSelf.avPlayer!.play()
                    }
                }
            }else{
                strongSelf.avPlayerPlayComplete()
            }
        }
        
//        avPlayerTimeObserver = avPlayer!.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 4), queue: DispatchQueue.global()) {[weak self] time in
//         //    进度监控
//            BFLog(1, message: "cont:\(CMTimeGetSeconds(time) - CMTimeGetSeconds((self?.musicPlayRanges?.first!.start)!)), curr:\(CMTimeGetSeconds(time))")
//        } as? NSKeyValueObservation
    }
    
    func avplayerMusic(){
        let playerItem = self.avPlayer?.currentItem
        let rangeee = CMTimeRangeMake(start: playeTimeRange.start, duration: self.musicPlayRanges?.first?.duration ?? CMTime(value: 1200, timescale: 40))
        playerItem?.reversePlaybackEndTime = rangeee.start
        playerItem?.forwardPlaybackEndTime = CMTimeMinimum(self.musicPlayRanges?.first?.end ?? CMTime(value: 1000, timescale: 1), playeTimeRange.end)

        playerItem?.seek(to: playerItem!.reversePlaybackEndTime) { [weak self]_ in
            self?.avPlayer?.play()
        }
    }
    
    func avPlayerPlayComplete(){
        self.avPlayer?.pause()
        self.indexRage = 0
        let playerItem = self.avPlayer?.currentItem
        let rangeee = CMTimeRangeMake(start: playeTimeRange.start, duration: self.musicPlayRanges?.first?.duration ?? CMTime(value: 1200, timescale: 40))
        playerItem?.reversePlaybackEndTime = rangeee.start
        playerItem?.seek(to: playerItem?.reversePlaybackEndTime ?? CMTime(value: 0, timescale: 40))
    }
    
}

