问题
I am playing mp3 urls in Avplayer. It is working fine, and I am showing Slider for progress of the audio player. While trying to moving forward slider, It is getting jumpy. But audio seeking is working fine. Only slider UI getting jumpy.
My code is
/* ----------------------------------------------------
Player setup
------------------------------------------------------ */
func playerSetup(url:String) {
do {
let audioURL = url
avPlayerItem = AVPlayerItem( url:NSURL( string:audioURL )! as URL )
avPlayer = AVPlayer(playerItem:avPlayerItem)
avPlayer?.rate = 1.0
avPlayer?.play()
self.sliderSetup()
self.timeObserverSetup()
}
}
/* ----------------------------------------------------
Slider setup
------------------------------------------------------ */
func sliderSetup() {
DispatchQueue.global(qos: .background).async {
let duration : CMTime = self.avPlayerItem!.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
DispatchQueue.main.async {
self.slider!.maximumValue = Float(seconds)
}
}
slider.isContinuous = false
slider!.minimumValue = 0
slider!.value = 0
slider.addTarget(self, action: #selector(MyViewController.playbackSliderValueChanged(_:event:)), for: .valueChanged)
}
/* ----------------------------------------------------
Avplayer Observer timer setup
------------------------------------------------------ */
func timeObserverSetup() {
let interval = CMTime(seconds: 0.05, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
timeObserver = avPlayer!.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { [weak self] elapsedTime in
// print("observer running")
self!.updateSlider(elapsedTime: elapsedTime)
})
}
/* ----------------------------------------------------
update slider with avplayer duration
------------------------------------------------------ */
func updateSlider(elapsedTime: CMTime) {
let playerDuration = playerItemDuration()
if CMTIME_IS_INVALID(playerDuration) {
slider.minimumValue = 0.0
return
}
let duration = Float(CMTimeGetSeconds(playerDuration))
if duration.isFinite && duration > 0 {
slider.minimumValue = 0.0
slider.maximumValue = duration
let time = Float(CMTimeGetSeconds(elapsedTime))
slider.setValue(time, animated: true)
}
}
/* ----------------------------------------------------
avplayeritem duration
------------------------------------------------------ */
private func playerItemDuration() -> CMTime {
let thePlayerItem = avPlayer?.currentItem
if thePlayerItem?.status == .readyToPlay {
return thePlayerItem!.duration
}
return CMTime.invalid
}
/* ----------------------------------------------------
Slider Action
------------------------------------------------------ */
@objc func playbackSliderValueChanged(_ playbackSlider:UISlider, event: UIEvent) {
DispatchQueue.main.async {
let seconds : Int64 = Int64(self.slider.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
self.avPlayer!.seek(to: targetTime)
if let touchEvent = event.allTouches?.first {
switch touchEvent.phase {
case .began:
self.removeTimerObserver()
self.pauseAudio()
break
case .moved:
break
case .ended:
self.timeObserverSetup()
self.playAudio()
break
default:
break
}
}
}
}
/* ----------------------------------------------------
Remove timer observer
------------------------------------------------------ */
func removeTimerObserver() {
if ((self.timeObserver) != nil) {
if self.avPlayer?.rate == 1.0 { // it is required as you have to check if player is playing
self.avPlayer?.removeTimeObserver(self.timeObserver as Any)
self.timeObserver = nil
}
}
}
I have seen other forums, They suggested while began the slider touch, We can remove Time observer and then slider touch ends we can add observer, So, I have followed that.
But, Still it's getting issue.
Any suggestions?
回答1:
I have figured out the issue.
The issue is happening due to below property from slider.
slider.isContinuous = false
So, this I have removed, Now its working fine.
Also I am running slider action method in DispatchQueue.main.async. So, I changed it to following.
/* ----------------------------------------------------
Slider Action
------------------------------------------------------ */
@objc func playbackSliderValueChanged(_ playbackSlider:UISlider, event: UIEvent) {
let seconds : Int64 = Int64(self.slider.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
self.avPlayer!.seek(to: targetTime)
if let touchEvent = event.allTouches?.first {
switch touchEvent.phase {
case .began:
self.removeTimerObserver()
self.pauseAudio()
break
case .moved:
break
case .ended:
self.timeObserverSetup()
self.playAudio()
break
default:
break
}
}
}
Now it is working fine.
来源:https://stackoverflow.com/questions/62774834/moving-slider-getting-jumpy-while-playing-avplayer-in-ios