|
@@ -18,7 +18,11 @@ public class PQMovieInput: ImageSource {
|
|
|
|
|
|
public let targets = TargetContainer()
|
|
public let targets = TargetContainer()
|
|
public var runBenchmark = false
|
|
public var runBenchmark = false
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ var avPlayer:AVPlayer?
|
|
|
|
+ var indexRage = 0
|
|
|
|
+ var musicPlayRanges:[CMTimeRange]?
|
|
|
|
+
|
|
public weak var delegate: PQMovieInputDelegate?
|
|
public weak var delegate: PQMovieInputDelegate?
|
|
|
|
|
|
public var audioEncodingTarget: AudioEncodingTarget? {
|
|
public var audioEncodingTarget: AudioEncodingTarget? {
|
|
@@ -75,7 +79,9 @@ public class PQMovieInput: ImageSource {
|
|
public var readingShouldWait = false
|
|
public var readingShouldWait = false
|
|
public var videoInputStatusObserver: NSKeyValueObservation?
|
|
public var videoInputStatusObserver: NSKeyValueObservation?
|
|
public var audioInputStatusObserver: NSKeyValueObservation?
|
|
public var audioInputStatusObserver: NSKeyValueObservation?
|
|
-
|
|
|
|
|
|
+// var audioPlayerObserver : NSKeyValueObservation?
|
|
|
|
+ var avPlayerTimeObserver: NSKeyValueObservation?
|
|
|
|
+
|
|
public var useRealtimeThreads = false
|
|
public var useRealtimeThreads = false
|
|
public var timebaseInfo = mach_timebase_info_data_t()
|
|
public var timebaseInfo = mach_timebase_info_data_t()
|
|
|
|
|
|
@@ -154,15 +160,16 @@ public class PQMovieInput: ImageSource {
|
|
displayLink = CADisplayLink(target: self, selector: #selector(displayLinkClick(_:)))
|
|
displayLink = CADisplayLink(target: self, selector: #selector(displayLinkClick(_:)))
|
|
// 设置触发频率 这个周期可以通过frameInterval属性设置,CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2,每秒调用就变成30次
|
|
// 设置触发频率 这个周期可以通过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)
|
|
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)
|
|
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.videoInputStatusObserver?.invalidate()
|
|
self.audioInputStatusObserver?.invalidate()
|
|
self.audioInputStatusObserver?.invalidate()
|
|
|
|
+ self.avPlayerTimeObserver?.invalidate()
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: -
|
|
// MARK: -
|
|
@@ -304,10 +312,13 @@ public class PQMovieInput: ImageSource {
|
|
|
|
|
|
mIsExport = isExport
|
|
mIsExport = isExport
|
|
do {
|
|
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 {
|
|
} catch {
|
|
@@ -350,6 +361,8 @@ public class PQMovieInput: ImageSource {
|
|
displayLink = nil
|
|
displayLink = nil
|
|
|
|
|
|
beginTime = 0
|
|
beginTime = 0
|
|
|
|
+
|
|
|
|
+ avPlayer?.pause()
|
|
}
|
|
}
|
|
|
|
|
|
public func resume() {
|
|
public func resume() {
|
|
@@ -359,6 +372,7 @@ public class PQMovieInput: ImageSource {
|
|
|
|
|
|
// 暂停帧的刷新 true:停 ; false:开始
|
|
// 暂停帧的刷新 true:停 ; false:开始
|
|
displayLink?.isPaused = false
|
|
displayLink?.isPaused = false
|
|
|
|
+ avPlayer?.play()
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -391,7 +405,10 @@ public class PQMovieInput: ImageSource {
|
|
FilterLog(message: "暂停时间:\(currTime)")
|
|
FilterLog(message: "暂停时间:\(currTime)")
|
|
|
|
|
|
beginTime = 0
|
|
beginTime = 0
|
|
|
|
+
|
|
|
|
+ avPlayer?.pause()
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
|
|
// MARK: -
|
|
// MARK: -
|
|
|
|
|
|
@@ -459,6 +476,8 @@ public class PQMovieInput: ImageSource {
|
|
}
|
|
}
|
|
|
|
|
|
func readNextAudioSample(with assetReader: AVAssetReader, from audioTrackOutput: AVAssetReaderOutput) {
|
|
func readNextAudioSample(with assetReader: AVAssetReader, from audioTrackOutput: AVAssetReaderOutput) {
|
|
|
|
+
|
|
|
|
+ return
|
|
if !isPlay {
|
|
if !isPlay {
|
|
FilterLog(message: "自动停到首帧的不处理音频")
|
|
FilterLog(message: "自动停到首帧的不处理音频")
|
|
return
|
|
return
|
|
@@ -470,13 +489,12 @@ public class PQMovieInput: ImageSource {
|
|
case cancelled = 4
|
|
case cancelled = 4
|
|
*/
|
|
*/
|
|
FilterLog(message: "音频解码状态\(assetReader.status.rawValue)")
|
|
FilterLog(message: "音频解码状态\(assetReader.status.rawValue)")
|
|
- if(assetReader.status == .completed){
|
|
|
|
- FilterLog(message: "提前结束的了!!!!\(assetReader.error)")
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
|
|
autoreleasepool {
|
|
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 {
|
|
if let movieOutput = synchronizedMovieOutput {
|
|
FilterLog(message: "this is runing assetWriterAudioInput markAsFinished \(String(describing: assetReader.error)) \(assetReader)")
|
|
FilterLog(message: "this is runing assetWriterAudioInput markAsFinished \(String(describing: assetReader.error)) \(assetReader)")
|
|
|
|
|
|
@@ -534,6 +552,7 @@ public class PQMovieInput: ImageSource {
|
|
if mAutoPause {
|
|
if mAutoPause {
|
|
mAutoPause = false
|
|
mAutoPause = false
|
|
pause()
|
|
pause()
|
|
|
|
+ avPlayerPlayComplete()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -641,3 +660,112 @@ public class PQMovieInput: ImageSource {
|
|
if synchronizedMovieOutput != nil, synchronizedEncodingDebug { print(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
|
|
|
|
+
|
|
|
|
+ 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 {
|
|
|
|
+////
|
|
|
|
+//// }
|
|
|
|
+//
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|