Anilkumar iOS Developer
Anilkumar iOS Developer

Reputation: 3745

Moving slider getting jumpy while playing Avplayer in iOS

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.

enter image description here

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?

Upvotes: 0

Views: 2818

Answers (1)

Anilkumar iOS Developer
Anilkumar iOS Developer

Reputation: 3745

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.

Upvotes: 4

Related Questions