diff --git a/Sources/KSPlayer/AVPlayer/KSOptions.swift b/Sources/KSPlayer/AVPlayer/KSOptions.swift index c1ae4aa9d..141d11e59 100644 --- a/Sources/KSPlayer/AVPlayer/KSOptions.swift +++ b/Sources/KSPlayer/AVPlayer/KSOptions.swift @@ -157,8 +157,8 @@ open class KSOptions { // 缓冲算法函数 open func playable(capacitys: [CapacityProtocol], isFirst: Bool, isSeek: Bool) -> LoadingState { - let packetCount = capacitys.map(\.packetCount).max() ?? 0 - let frameCount = capacitys.map(\.frameCount).max() ?? 0 + let packetCount = capacitys.map(\.packetCount).min() ?? 0 + let frameCount = capacitys.map(\.frameCount).min() ?? 0 let isEndOfFile = capacitys.allSatisfy(\.isEndOfFile) let loadedTime = capacitys.map(\.loadedTime).min() ?? 0 let progress = loadedTime * 100.0 / preferredForwardBufferDuration @@ -166,10 +166,6 @@ open class KSOptions { if capacity.isEndOfFile && capacity.packetCount == 0 { return true } - // 处理视频轨道一致没有值的问题(纯音频) - if capacitys.count > 1, capacity.mediaType == .video && capacity.frameCount == 0 && capacity.packetCount == 0 { - return true - } guard capacity.frameCount >= capacity.frameMaxCount >> 2 else { return false } @@ -184,8 +180,8 @@ open class KSOptions { if capacity.mediaType == .audio || isSecondOpen { if isFirst { return true - } else if isSeek, capacity.packetCount >= Int(capacity.fps) { - return true + } else { + return capacity.loadedTime >= preferredForwardBufferDuration / 2 } } } @@ -344,7 +340,7 @@ open class KSOptions { } // private var lastMediaTime = CACurrentMediaTime() - open func videoClockSync(main: KSClock, nextVideoTime: TimeInterval, fps: Float) -> ClockProcessType { + open func videoClockSync(main: KSClock, nextVideoTime: TimeInterval, fps: Float, frameCount: Int) -> ClockProcessType { var desire = main.getTime() - videoDelay #if !os(macOS) desire -= AVAudioSession.sharedInstance().outputLatency @@ -358,19 +354,33 @@ open class KSOptions { return .remain } else { if diff < -4 / Double(fps) { - KSLog("[video] video delay=\(diff), clock=\(desire), delay count=\(videoClockDelayCount)") videoClockDelayCount += 1 - if diff < -8, videoClockDelayCount % 100 == 0 { - KSLog("[video] video delay seek video track") - return .seek - } - if diff < -1, videoClockDelayCount % 10 == 0 { - return .flush - } - if videoClockDelayCount % 2 == 0 { - return .dropNext + let log = "[video] video delay=\(diff), clock=\(desire), delay count=\(videoClockDelayCount), frameCount=\(frameCount)" + if frameCount == 1 { + if diff < -1, videoClockDelayCount % 10 == 0 { + KSLog("\(log) drop gop Packet") + return .dropGOPPacket + } else if videoClockDelayCount % 5 == 0 { + KSLog("\(log) drop next frame") + return .dropNextFrame + } else { + return .next + } } else { - return .next + if diff < -8, videoClockDelayCount % 100 == 0 { + KSLog("\(log) seek video track") + return .seek + } + if diff < -1, videoClockDelayCount % 10 == 0 { + KSLog("\(log) flush video track") + return .flush + } + if videoClockDelayCount % 2 == 0 { + KSLog("\(log) drop next frame") + return .dropNextFrame + } else { + return .next + } } } else { videoClockDelayCount = 0 @@ -409,9 +419,12 @@ open class KSOptions { } open func liveAdaptivePlaybackRate(loadingState: LoadingState) -> Float? { - if loadingState.loadedTime > preferredForwardBufferDuration + 4 { + if loadingState.isFirst { + return nil + } + if loadingState.loadedTime > preferredForwardBufferDuration + 5 { return 1.2 - } else if loadingState.loadedTime < preferredForwardBufferDuration { + } else if loadingState.loadedTime < preferredForwardBufferDuration / 2 { return 0.8 } else { return 1 diff --git a/Sources/KSPlayer/AVPlayer/PlayerDefines.swift b/Sources/KSPlayer/AVPlayer/PlayerDefines.swift index b7ccce9a7..f28af183f 100644 --- a/Sources/KSPlayer/AVPlayer/PlayerDefines.swift +++ b/Sources/KSPlayer/AVPlayer/PlayerDefines.swift @@ -148,7 +148,9 @@ public struct VideoAdaptationState { public enum ClockProcessType { case remain case next - case dropNext + case dropNextFrame + case dropNextPacket + case dropGOPPacket case flush case seek } diff --git a/Sources/KSPlayer/MEPlayer/AudioGraphPlayer.swift b/Sources/KSPlayer/MEPlayer/AudioGraphPlayer.swift new file mode 100644 index 000000000..0e0cea01e --- /dev/null +++ b/Sources/KSPlayer/MEPlayer/AudioGraphPlayer.swift @@ -0,0 +1,323 @@ +// +// AudioGraphPlayer.swift +// KSPlayer +// +// Created by kintan on 2018/3/16. +// + +import AudioToolbox +import AVFAudio +import CoreAudio + +public final class AudioGraphPlayer: AudioPlayer, FrameOutput { + private let graph: AUGraph + private var audioUnitForMixer: AudioUnit! + private var audioUnitForTimePitch: AudioUnit! + private var audioUnitForDynamicsProcessor: AudioUnit! + private var audioUnitForOutput: AudioUnit! + private var currentRenderReadOffset = UInt32(0) + private var sourceNodeAudioFormat: AVAudioFormat? + private var sampleSize = UInt32(MemoryLayout.size) + #if os(macOS) + private var volumeBeforeMute: Float = 0.0 + #endif + weak var renderSource: OutputRenderSourceDelegate? + private var currentRender: AudioFrame? { + didSet { + if currentRender == nil { + currentRenderReadOffset = 0 + } + } + } + + func play(time _: TimeInterval) { + AUGraphStart(graph) + } + + func pause() { + AUGraphStop(graph) + } + + var playbackRate: Float { + get { + var playbackRate = AudioUnitParameterValue(0.0) + AudioUnitGetParameter(audioUnitForTimePitch, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, &playbackRate) + return playbackRate + } + set { + AudioUnitSetParameter(audioUnitForTimePitch, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, newValue, 0) + } + } + + var volume: Float { + get { + var volume = AudioUnitParameterValue(0.0) + #if os(macOS) + let inID = kStereoMixerParam_Volume + #else + let inID = kMultiChannelMixerParam_Volume + #endif + AudioUnitGetParameter(audioUnitForMixer, inID, kAudioUnitScope_Input, 0, &volume) + return volume + } + set { + #if os(macOS) + let inID = kStereoMixerParam_Volume + #else + let inID = kMultiChannelMixerParam_Volume + #endif + AudioUnitSetParameter(audioUnitForMixer, inID, kAudioUnitScope_Input, 0, newValue, 0) + } + } + + public var isMuted: Bool { + get { + var value = AudioUnitParameterValue(1.0) + #if os(macOS) + AudioUnitGetParameter(audioUnitForMixer, kStereoMixerParam_Volume, kAudioUnitScope_Input, 0, &value) + #else + AudioUnitGetParameter(audioUnitForMixer, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 0, &value) + #endif + return value == 0 + } + set { + let value = newValue ? 0 : 1 + #if os(macOS) + if value == 0 { + volumeBeforeMute = volume + } + AudioUnitSetParameter(audioUnitForMixer, kStereoMixerParam_Volume, kAudioUnitScope_Input, 0, min(Float(value), volumeBeforeMute), 0) + #else + AudioUnitSetParameter(audioUnitForMixer, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 0, AudioUnitParameterValue(value), 0) + #endif + } + } + + public var attackTime: Float { + get { + var value = AudioUnitParameterValue(1.0) + AudioUnitGetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_AttackTime, kAudioUnitScope_Global, 0, &value) + return value + } + set { + AudioUnitSetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_AttackTime, kAudioUnitScope_Global, 0, AudioUnitParameterValue(newValue), 0) + } + } + + public var releaseTime: Float { + get { + var value = AudioUnitParameterValue(1.0) + AudioUnitGetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_ReleaseTime, kAudioUnitScope_Global, 0, &value) + return value + } + set { + AudioUnitSetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_ReleaseTime, kAudioUnitScope_Global, 0, AudioUnitParameterValue(newValue), 0) + } + } + + public var threshold: Float { + get { + var value = AudioUnitParameterValue(1.0) + AudioUnitGetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_Threshold, kAudioUnitScope_Global, 0, &value) + return value + } + set { + AudioUnitSetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_Threshold, kAudioUnitScope_Global, 0, AudioUnitParameterValue(newValue), 0) + } + } + + public var expansionRatio: Float { + get { + var value = AudioUnitParameterValue(1.0) + AudioUnitGetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_ExpansionRatio, kAudioUnitScope_Global, 0, &value) + return value + } + set { + AudioUnitSetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_ExpansionRatio, kAudioUnitScope_Global, 0, AudioUnitParameterValue(newValue), 0) + } + } + + public var overallGain: Float { + get { + var value = AudioUnitParameterValue(1.0) + AudioUnitGetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_OverallGain, kAudioUnitScope_Global, 0, &value) + return value + } + set { + AudioUnitSetParameter(audioUnitForDynamicsProcessor, kDynamicsProcessorParam_OverallGain, kAudioUnitScope_Global, 0, AudioUnitParameterValue(newValue), 0) + } + } + + init() { + var newGraph: AUGraph! + NewAUGraph(&newGraph) + graph = newGraph + var descriptionForTimePitch = AudioComponentDescription() + descriptionForTimePitch.componentType = kAudioUnitType_FormatConverter + descriptionForTimePitch.componentSubType = kAudioUnitSubType_NewTimePitch + descriptionForTimePitch.componentManufacturer = kAudioUnitManufacturer_Apple + var descriptionForDynamicsProcessor = AudioComponentDescription() + descriptionForDynamicsProcessor.componentType = kAudioUnitType_Effect + descriptionForDynamicsProcessor.componentManufacturer = kAudioUnitManufacturer_Apple + descriptionForDynamicsProcessor.componentSubType = kAudioUnitSubType_DynamicsProcessor + var descriptionForMixer = AudioComponentDescription() + descriptionForMixer.componentType = kAudioUnitType_Mixer + descriptionForMixer.componentManufacturer = kAudioUnitManufacturer_Apple + #if os(macOS) + descriptionForMixer.componentSubType = kAudioUnitSubType_StereoMixer + #else + descriptionForMixer.componentSubType = kAudioUnitSubType_MultiChannelMixer + #endif + var descriptionForOutput = AudioComponentDescription() + descriptionForOutput.componentType = kAudioUnitType_Output + descriptionForOutput.componentManufacturer = kAudioUnitManufacturer_Apple + #if os(macOS) + descriptionForOutput.componentSubType = kAudioUnitSubType_DefaultOutput + #else + descriptionForOutput.componentSubType = kAudioUnitSubType_RemoteIO + #endif + var nodeForTimePitch = AUNode() + var nodeForDynamicsProcessor = AUNode() + var nodeForMixer = AUNode() + var nodeForOutput = AUNode() + AUGraphAddNode(graph, &descriptionForTimePitch, &nodeForTimePitch) + AUGraphAddNode(graph, &descriptionForMixer, &nodeForMixer) + AUGraphAddNode(graph, &descriptionForDynamicsProcessor, &nodeForDynamicsProcessor) + AUGraphAddNode(graph, &descriptionForOutput, &nodeForOutput) + AUGraphOpen(graph) + AUGraphConnectNodeInput(graph, nodeForTimePitch, 0, nodeForDynamicsProcessor, 0) + AUGraphConnectNodeInput(graph, nodeForDynamicsProcessor, 0, nodeForMixer, 0) + AUGraphConnectNodeInput(graph, nodeForMixer, 0, nodeForOutput, 0) + AUGraphNodeInfo(graph, nodeForTimePitch, &descriptionForTimePitch, &audioUnitForTimePitch) + AUGraphNodeInfo(graph, nodeForDynamicsProcessor, &descriptionForDynamicsProcessor, &audioUnitForDynamicsProcessor) + AUGraphNodeInfo(graph, nodeForMixer, &descriptionForMixer, &audioUnitForMixer) + AUGraphNodeInfo(graph, nodeForOutput, &descriptionForOutput, &audioUnitForOutput) + var inputCallbackStruct = renderCallbackStruct() + AUGraphSetNodeInputCallback(graph, nodeForTimePitch, 0, &inputCallbackStruct) + addRenderNotify(audioUnit: audioUnitForOutput) + } + + func prepare(audioFormat: AVAudioFormat) { + if sourceNodeAudioFormat == audioFormat { + return + } + sourceNodeAudioFormat = audioFormat + sampleSize = audioFormat.sampleSize + var audioStreamBasicDescription = audioFormat.formatDescription.audioStreamBasicDescription + let audioStreamBasicDescriptionSize = UInt32(MemoryLayout.size) + var audioPlayerMaximumFramesPerSlice = AVAudioFrameCount(4096) + let inDataSize = UInt32(MemoryLayout.size(ofValue: audioPlayerMaximumFramesPerSlice)) + [audioUnitForTimePitch, audioUnitForDynamicsProcessor, audioUnitForMixer, audioUnitForOutput].forEach { unit in + guard let unit else { return } + AudioUnitSetProperty(unit, + kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Global, 0, + &audioPlayerMaximumFramesPerSlice, + inDataSize) + AudioUnitSetProperty(unit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, + &audioStreamBasicDescription, + audioStreamBasicDescriptionSize) + if unit != audioUnitForOutput { + AudioUnitSetProperty(unit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, 0, + &audioStreamBasicDescription, + audioStreamBasicDescriptionSize) + } + } + AUGraphInitialize(graph) + } + + func flush() { + currentRender = nil + } + + deinit { + AUGraphStop(graph) + AUGraphUninitialize(graph) + AUGraphClose(graph) + DisposeAUGraph(graph) + } +} + +extension AudioGraphPlayer { + private func renderCallbackStruct() -> AURenderCallbackStruct { + var inputCallbackStruct = AURenderCallbackStruct() + inputCallbackStruct.inputProcRefCon = Unmanaged.passUnretained(self).toOpaque() + inputCallbackStruct.inputProc = { refCon, _, _, _, inNumberFrames, ioData in + guard let ioData else { + return noErr + } + let `self` = Unmanaged.fromOpaque(refCon).takeUnretainedValue() + self.audioPlayerShouldInputData(ioData: UnsafeMutableAudioBufferListPointer(ioData), numberOfFrames: inNumberFrames) + return noErr + } + return inputCallbackStruct + } + + private func addRenderNotify(audioUnit: AudioUnit) { + AudioUnitAddRenderNotify(audioUnit, { refCon, ioActionFlags, inTimeStamp, _, _, _ in + let `self` = Unmanaged.fromOpaque(refCon).takeUnretainedValue() + autoreleasepool { + if ioActionFlags.pointee.contains(.unitRenderAction_PostRender) { + self.audioPlayerDidRenderSample(sampleTimestamp: inTimeStamp.pointee) + } + } + return noErr + }, Unmanaged.passUnretained(self).toOpaque()) + } + + private func audioPlayerShouldInputData(ioData: UnsafeMutableAudioBufferListPointer, numberOfFrames: UInt32) { + var ioDataWriteOffset = 0 + var numberOfSamples = numberOfFrames + while numberOfSamples > 0 { + if currentRender == nil { + currentRender = renderSource?.getAudioOutputRender() + } + guard let currentRender else { + break + } + let residueLinesize = currentRender.numberOfSamples - currentRenderReadOffset + guard residueLinesize > 0 else { + self.currentRender = nil + continue + } + if sourceNodeAudioFormat != currentRender.audioFormat { + runInMainqueue { [weak self] in + guard let self else { + return + } + self.prepare(audioFormat: currentRender.audioFormat) + } + return + } + let framesToCopy = min(numberOfSamples, residueLinesize) + let bytesToCopy = Int(framesToCopy * sampleSize) + let offset = Int(currentRenderReadOffset * sampleSize) + for i in 0 ..< min(ioData.count, currentRender.data.count) { + (ioData[i].mData! + ioDataWriteOffset).copyMemory(from: currentRender.data[i]! + offset, byteCount: bytesToCopy) + } + numberOfSamples -= framesToCopy + ioDataWriteOffset += bytesToCopy + currentRenderReadOffset += framesToCopy + } + let sizeCopied = (numberOfFrames - numberOfSamples) * sampleSize + for i in 0 ..< ioData.count { + let sizeLeft = Int(ioData[i].mDataByteSize - sizeCopied) + if sizeLeft > 0 { + memset(ioData[i].mData! + Int(sizeCopied), 0, sizeLeft) + } + } + } + + private func audioPlayerDidRenderSample(sampleTimestamp _: AudioTimeStamp) { + if let currentRender { + let currentPreparePosition = currentRender.position + currentRender.duration * Int64(currentRenderReadOffset) / Int64(currentRender.numberOfSamples) + if currentPreparePosition > 0 { + renderSource?.setAudio(time: currentRender.timebase.cmtime(for: currentPreparePosition)) + } + } + } +} diff --git a/Sources/KSPlayer/MEPlayer/CircularBuffer.swift b/Sources/KSPlayer/MEPlayer/CircularBuffer.swift index 4c7f51dd1..13f36b4aa 100644 --- a/Sources/KSPlayer/MEPlayer/CircularBuffer.swift +++ b/Sources/KSPlayer/MEPlayer/CircularBuffer.swift @@ -42,6 +42,9 @@ public class CircularBuffer { if destroyed { return } + if _buffer[Int(tailIndex & mask)] != nil { + assertionFailure("value is not nil of headIndex: \(headIndex),tailIndex: \(tailIndex), bufferCount: \(_buffer.count), mask: \(mask)") + } _buffer[Int(tailIndex & mask)] = value if sorted { // 不用sort进行排序,这个比较高效 @@ -74,7 +77,7 @@ public class CircularBuffer { } } - public func pop(wait: Bool = false, where predicate: ((Item) -> Bool)? = nil) -> Item? { + public func pop(wait: Bool = false, where predicate: ((Item, Int) -> Bool)? = nil) -> Item? { condition.lock() defer { condition.unlock() } if destroyed { @@ -95,7 +98,7 @@ public class CircularBuffer { assertionFailure("value is nil of index: \(index) headIndex: \(headIndex),tailIndex: \(tailIndex), bufferCount: \(_buffer.count), mask: \(mask)") return nil } - if let predicate, !predicate(item) { + if let predicate, !predicate(item, _count) { return nil } else { headIndex &+= 1 @@ -132,10 +135,8 @@ public class CircularBuffer { defer { condition.unlock() } headIndex = 0 tailIndex = 0 - if destroyed { - _buffer.removeAll(keepingCapacity: true) - _buffer.append(contentsOf: ContiguousArray(repeating: nil, count: maxCount)) - } + _buffer.removeAll(keepingCapacity: !destroyed) + _buffer.append(contentsOf: ContiguousArray(repeating: nil, count: destroyed ? 1 : maxCount)) condition.broadcast() } diff --git a/Sources/KSPlayer/MEPlayer/EmbedDataSouce.swift b/Sources/KSPlayer/MEPlayer/EmbedDataSouce.swift index 9cbfa6024..00e733c29 100644 --- a/Sources/KSPlayer/MEPlayer/EmbedDataSouce.swift +++ b/Sources/KSPlayer/MEPlayer/EmbedDataSouce.swift @@ -23,7 +23,7 @@ extension FFmpegAssetTrack: SubtitleInfo { extension FFmpegAssetTrack: KSSubtitleProtocol { public func search(for time: TimeInterval) -> [SubtitlePart] { if isImageSubtitle { - let part = subtitle?.outputRenderQueue.pop { item -> Bool in + let part = subtitle?.outputRenderQueue.pop { item, _ -> Bool in item.part < time || item.part == time }?.part if let part { diff --git a/Sources/KSPlayer/MEPlayer/Filter.swift b/Sources/KSPlayer/MEPlayer/Filter.swift index 48e1b0107..8a38caa13 100644 --- a/Sources/KSPlayer/MEPlayer/Filter.swift +++ b/Sources/KSPlayer/MEPlayer/Filter.swift @@ -33,12 +33,10 @@ class MEFilter { private func setup(filters: String) -> Bool { var inputs = avfilter_inout_alloc() var outputs = avfilter_inout_alloc() - defer { - avfilter_inout_free(&inputs) - avfilter_inout_free(&outputs) - } var ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs) guard ret >= 0, let graph, let inputs, let outputs else { + avfilter_inout_free(&inputs) + avfilter_inout_free(&outputs) return false } let bufferSink = avfilter_get_by_name(isAudio ? "abuffersink" : "buffersink") diff --git a/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift b/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift index 40dcfbefd..060e6175e 100644 --- a/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift +++ b/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift @@ -32,16 +32,7 @@ final class MEPlayerItem { private var isSeek = false private var allPlayerItemTracks = [PlayerItemTrackProtocol]() private var maxFrameDuration = 10.0 - private var videoAudioTracks: [CapacityProtocol] { - var tracks = [CapacityProtocol]() - if let audioTrack { - tracks.append(audioTrack) - } - if !options.videoDisable, let videoTrack { - tracks.append(videoTrack) - } - return tracks - } + private var videoAudioTracks = [CapacityProtocol]() private var audioRecognizer: AudioRecognizer? private var videoTrack: SyncPlayerItemTrack? @@ -331,6 +322,7 @@ extension MEPlayerItem { videoAdaptation = nil videoTrack = nil audioTrack = nil + videoAudioTracks.removeAll() assetTracks = (0 ..< Int(formatCtx.pointee.nb_streams)).compactMap { i in if let coreStream = formatCtx.pointee.streams[i] { coreStream.pointee.discard = AVDISCARD_ALL @@ -384,6 +376,9 @@ extension MEPlayerItem { track.delegate = self allPlayerItemTracks.append(track) videoTrack = track + if first.codecpar.codec_id != AV_CODEC_ID_MJPEG { + videoAudioTracks.append(track) + } if bitRates.count > 1, options.videoAdaptable { let bitRateState = VideoAdaptationState.BitRateState(bitRate: first.bitRate, time: CACurrentMediaTime()) videoAdaptation = VideoAdaptationState(bitRates: bitRates.sorted(by: <), duration: duration, fps: first.nominalFrameRate, bitRateStates: [bitRateState]) @@ -407,6 +402,7 @@ extension MEPlayerItem { track.delegate = self allPlayerItemTracks.append(track) audioTrack = track + videoAudioTracks.append(track) isAudioStalled = false } } @@ -750,9 +746,9 @@ extension MEPlayerItem: OutputRenderSourceDelegate { } let fps = videoTrack.fps var type: ClockProcessType = force ? .next : .remain - let predicate: ((VideoVTBFrame) -> Bool)? = force ? nil : { [weak self] frame -> Bool in + let predicate: ((VideoVTBFrame, Int) -> Bool)? = force ? nil : { [weak self] frame, count -> Bool in guard let self else { return true } - type = self.options.videoClockSync(main: self.mainClock(), nextVideoTime: frame.seconds, fps: fps) + type = self.options.videoClockSync(main: self.mainClock(), nextVideoTime: frame.seconds, fps: fps, frameCount: count) return type != .remain } let frame = videoTrack.getOutputRender(where: predicate) @@ -761,13 +757,36 @@ extension MEPlayerItem: OutputRenderSourceDelegate { break case .next: break - case .dropNext: + case .dropNextFrame: _ = videoTrack.getOutputRender(where: nil) case .flush: videoTrack.outputRenderQueue.flush() case .seek: videoTrack.outputRenderQueue.flush() videoTrack.seekTime = currentPlaybackTime + case .dropNextPacket: + if let videoTrack = videoTrack as? AsyncPlayerItemTrack { + _ = videoTrack.packetQueue.pop { item, _ -> Bool in + if let corePacket = item.corePacket { + return corePacket.pointee.flags & AV_PKT_FLAG_KEY != AV_PKT_FLAG_KEY + } else { + return false + } + } + } + case .dropGOPPacket: + if let videoTrack = videoTrack as? AsyncPlayerItemTrack { + var packet: Packet? = nil + repeat { + packet = videoTrack.packetQueue.pop { item, _ -> Bool in + if let corePacket = item.corePacket { + return corePacket.pointee.flags & AV_PKT_FLAG_KEY != AV_PKT_FLAG_KEY + } else { + return false + } + } + } while packet != nil + } } return frame } diff --git a/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift b/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift index 166d6a68c..96368488d 100644 --- a/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift +++ b/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift @@ -89,7 +89,7 @@ class SyncPlayerItemTrack: PlayerItemTrackProtocol, CustomString } } - func getOutputRender(where predicate: ((Frame) -> Bool)?) -> Frame? { + func getOutputRender(where predicate: ((Frame, Int) -> Bool)?) -> Frame? { let outputFecthRender = outputRenderQueue.pop(where: predicate) if outputFecthRender == nil { if state == .finished, frameCount == 0 { @@ -155,7 +155,7 @@ final class AsyncPlayerItemTrack: SyncPlayerItemTrack { private var decodeOperation: BlockOperation! // 无缝播放使用的PacketQueue private var loopPacketQueue: CircularBuffer? - private var packetQueue = CircularBuffer() + var packetQueue = CircularBuffer() override var packetCount: Int { packetQueue.count } override var isLoopModel: Bool { didSet {