Browse Source

修复音乐偶现无音问题:使用备选方案avplayer循环播放营业

huzhiqiang 3 năm trước cách đây
mục cha
commit
7259edc557

+ 145 - 17
BFFramework/Classes/PQGPUImage/akfilters/PQMovieInput.swift

@@ -18,7 +18,11 @@ public class PQMovieInput: ImageSource {
 
     public let targets = TargetContainer()
     public var runBenchmark = false
-
+    
+    var avPlayer:AVPlayer?
+    var indexRage = 0
+    var musicPlayRanges:[CMTimeRange]?
+    
     public weak var delegate: PQMovieInputDelegate?
 
     public var audioEncodingTarget: AudioEncodingTarget? {
@@ -75,7 +79,9 @@ public class PQMovieInput: ImageSource {
     public  var readingShouldWait = false
     public   var videoInputStatusObserver: NSKeyValueObservation?
     public var audioInputStatusObserver: NSKeyValueObservation?
-
+//    var audioPlayerObserver : NSKeyValueObservation?
+    var avPlayerTimeObserver: NSKeyValueObservation?
+    
     public var useRealtimeThreads = false
     public  var timebaseInfo = mach_timebase_info_data_t()
 
@@ -154,15 +160,16 @@ public class PQMovieInput: ImageSource {
         displayLink = CADisplayLink(target: self, selector: #selector(displayLinkClick(_:)))
         // 设置触发频率 这个周期可以通过frameInterval属性设置,CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2,每秒调用就变成30次
 
-        displayLink?.frameInterval = 2
-        // 加入循环 要使用 common 不要让级别高的卡住回调事件
-        displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
-        displayLink?.isPaused = true
-        if #available(iOS 10.0, *) {
+        if #available(iOS 10, *){
+            displayLink?.preferredFramesPerSecond = 30
             seekQueue = DispatchQueue(label: "PQ.MovieInput.seeking", qos: .userInteractive, attributes: .initiallyInactive, autoreleaseFrequency: .never, target: nil)
-        } else {
+        }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
     }
 
     // 初始化方法
@@ -182,6 +189,7 @@ public class PQMovieInput: ImageSource {
 
         self.videoInputStatusObserver?.invalidate()
         self.audioInputStatusObserver?.invalidate()
+        self.avPlayerTimeObserver?.invalidate()
     }
 
     // MARK: -
@@ -304,10 +312,13 @@ public class PQMovieInput: ImageSource {
 
         mIsExport = isExport
         do {
-            try NSObject.catchException {
-                guard self.assetReader.startReading() else {
-                    FilterLog(message: "ERROR: Unable to start reading: \(String(describing: self.assetReader.error))")
-                    return
+            try NSObject.catchException { [self] in
+//                guard self.assetReader.startReading() else {
+//                    FilterLog(message: "ERROR: Unable to start reading: \(String(describing: self.assetReader.error))")
+//                    return
+//                }
+                if !mAutoPause {
+                    avplayerMusic()
                 }
             }
         } catch {
@@ -350,6 +361,8 @@ public class PQMovieInput: ImageSource {
         displayLink = nil
 
         beginTime = 0
+        
+        avPlayer?.pause()
     }
 
     public func resume() {
@@ -359,6 +372,7 @@ public class PQMovieInput: ImageSource {
 
         // 暂停帧的刷新 true:停 ; false:开始
         displayLink?.isPaused = false
+        avPlayer?.play()
        
     }
 
@@ -391,7 +405,10 @@ public class PQMovieInput: ImageSource {
         FilterLog(message: "暂停时间:\(currTime)")
 
         beginTime = 0
+        
+        avPlayer?.pause()
     }
+    
 
     // MARK: -
 
@@ -459,6 +476,8 @@ public class PQMovieInput: ImageSource {
     }
 
     func readNextAudioSample(with assetReader: AVAssetReader, from audioTrackOutput: AVAssetReaderOutput) {
+        
+        return
         if !isPlay {
             FilterLog(message: "自动停到首帧的不处理音频")
             return
@@ -470,13 +489,12 @@ public class PQMovieInput: ImageSource {
          case cancelled = 4
          */
         FilterLog(message: "音频解码状态\(assetReader.status.rawValue)")
-        if(assetReader.status == .completed){
-            FilterLog(message: "提前结束的了!!!!\(assetReader.error)")
- 
-        }
         
         autoreleasepool {
-            guard let sampleBuffer = audioTrackOutput.copyNextSampleBuffer() else {
+            guard let sampleBuffer = audioTrackOutput.copyNextSampleBuffer(),CMSampleBufferIsValid(sampleBuffer) else {
+                if(assetReader.status == .completed){
+                    FilterLog(message: "提前结束的了!!!!\(assetReader.error)")
+                }
                 if let movieOutput = synchronizedMovieOutput {
                     FilterLog(message: "this is runing assetWriterAudioInput  markAsFinished \(String(describing: assetReader.error)) \(assetReader)")
 
@@ -534,6 +552,7 @@ public class PQMovieInput: ImageSource {
             if mAutoPause {
                 mAutoPause = false
                 pause()
+                avPlayerPlayComplete()
             }
         }
     }
@@ -641,3 +660,112 @@ public class PQMovieInput: ImageSource {
         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
+
+        playerItem.forwardPlaybackEndTime = range?.end ?? CMTime(value: 0, timescale: 1404000)
+        playerItem.reversePlaybackEndTime = range?.start ?? CMTime(value: 2000, timescale: 100)
+        playerItem.seek(to: playerItem.reversePlaybackEndTime)
+
+        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{
+                
+//                let range = ranges[strongSelf.indexRage]
+
+                playerItem.forwardPlaybackEndTime = range!.end
+                playerItem.reversePlaybackEndTime = range!.start
+                HHZPrint("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
+            // 进度监控
+            HHZPrint("index:\(self?.indexRage), curr:\(CMTimeGetSeconds(time))")
+        } as? NSKeyValueObservation
+    }
+    
+    func avplayerMusic(){
+        let playerItem = self.avPlayer?.currentItem
+        playerItem!.seek(to: playerItem!.reversePlaybackEndTime) { [weak self]_ in
+            self?.avPlayer?.play()
+        }
+    }
+    
+    func avPlayerPlayComplete(){
+        self.avPlayer!.pause()
+        self.indexRage = 0
+        let playerItem = self.avPlayer?.currentItem
+        playerItem?.seek(to: playerItem?.reversePlaybackEndTime ?? CMTime(value: 0, timescale: 40))
+    }
+    
+    
+//    func playMusic(assetUrl:URL, ranges:[CMTimeRange]){
+//        if aplayer != nil {
+//            var indexRange = 0
+//            var range = ranges.first
+//            aplayer?.currentTime = CMTimeGetSeconds(range!.start)
+//            aplayer?.play()
+//            HHZPrint("声道数:\(aplayer?.numberOfChannels)")
+//            return
+//
+////                audioPlayerObserver?.invalidate()
+//            audioPlayerObserver = aplayer!.observe(\.deviceCurrentTime, options: [.new], changeHandler: { player, change in
+//                let currt = change.newValue ?? 0
+//                let end = CMTimeGetSeconds(range?.end ?? CMTime(value: 1, timescale: 100))
+//                HHZPrint("curr:\(currt), end:\(end), dur:\(player.duration)")
+//                if currt >= end || currt == player.duration {
+//                    player.pause()
+//                    indexRange += 1
+//                    if indexRange < ranges.count{
+//                        range = ranges[indexRange]
+//                        player.currentTime = CMTimeGetSeconds(range!.start)
+//                        player.play()
+//                    }
+//                }
+//            })
+//            let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in
+//                HHZPrint("\(self?.aplayer?.currentTime)")
+//            }
+//            RunLoop.current.add(timer, forMode: .common)
+//
+//        }
+////        let queue = DispatchQueue.init(label: "playMusic")
+////
+////        queue.async {
+////
+////        }
+//
+//    }
+    
+    
+}
+

+ 1 - 1
BFFramework/Classes/Stuckpoint/Controller/PQStuckPointEditerController.swift

@@ -1454,7 +1454,7 @@ extension PQStuckPointEditerController {
                                 // 最后一点素材时长
                                 let lastAssetDuration = Float(CMTimeGetSeconds(asset.duration)) - useAssestDurationTemp
 
-                                lastPointIndex = (sticker.clipCount % stuckPointsTemp.count)
+                                let lastPointIndex = (sticker.clipCount % stuckPointsTemp.count)
                                 // 两个卡点
                                 let a: Float = stuckPointsTemp[lastPointIndex ?? 0]
                                 var b: Float = 0.0

+ 6 - 9
BFFramework/Classes/Stuckpoint/ViewModel/PQGPUImagePlayerView.swift

@@ -377,14 +377,16 @@ public class PQGPUImagePlayerView: UIView {
         var audioSettings: [String: Any] = [
             AVFormatIDKey: kAudioFormatLinearPCM,
         ]
-        if #available(iOS 14.0, *) {
+//        if #available(iOS 14.0, *) {
             audioSettings[AVLinearPCMIsFloatKey] = false
             audioSettings[AVLinearPCMBitDepthKey] = 16
-        }
+//        }
         do {
             if composition != nil {
                 FilterLog(message: "composition 方式初始化")
                 movie = try PQMovieInput(asset: composition!, videoComposition: videoComposition, audioMix: audioMix, playAtActualSpeed: true, loop: isLoop, audioSettings: audioSettings)
+//                movie?.exportAudioUrl = url // clipAudioRange
+                movie?.configAVPlayer(assetUrl: url, ranges: [clipAudioRange])
             } else {
                 movie = try PQMovieInput(url: url, playAtActualSpeed: true, loop: isLoop, audioSettings: audioSettings)
 
@@ -463,13 +465,8 @@ public class PQGPUImagePlayerView: UIView {
         FilterLog(message: "原素材 总数:\(mStickers?.count ?? 0) ")
        
         if mStickers?.count ?? 0 > 0 {
-            
-            for (index , sticker) in mStickers!.enumerated() {
-                BFLog(message: "mStickers timelinein:\(sticker.timelineIn) timelineout: \(sticker.timelineOut) index : \(index)")
-
-            }
-            
             for (index, currentSticker) in mStickers!.enumerated() {
+                BFLog(message: "mStickers timelinein:\(currentSticker.timelineIn) timelineout: \(currentSticker.timelineOut) index : \(index)")
                //到达最大缓存数退出
                 if cacheFilters.count == cacheFiltersMaxCount {
                     break
@@ -690,7 +687,7 @@ public extension PQGPUImagePlayerView {
         guard status != .pause else {
             return
         }
-        movie?.pause()
+        movie?.pause()  // 可能会引起crash: configureThread()里timebaseInfo为0,除法出错
         speaker?.pause()
         status = .pause
         showPlayBtn(isHidden: false)