Search code examples
swiftmacosavfoundationavkitavplayerlooper

"advanceToNextItem()" doesn't allow to loop through ALL videos


I've got auto-playing sequence of video clips with looping behaviour built on KVO. But, in addition to auto-playback, I'd like to advance all the clips using NSButton. So, if I press the Next Video button when my sequence is auto-playing, this action advances me to the next clip in a queue. But it doesn't work a proper way.

The problem is: when the queue comes to end, there's no loop for all the clips in the queue. There's a loop for the first clip only (if I pressed the Next Video button 10 times – only first clip loops 10 times), after that all clips are auto-playing normally.

Question: How to (when advancing) loop through all videos using NSButton?

Here's my code:

import Cocoa
import AVFoundation
import AVKit

class ViewController: NSViewController {

    @IBOutlet weak var avPlayerView: AVPlayerView!
    @IBOutlet weak var nextVideoButton: NSButton!
    @objc private let player = AVQueuePlayer()
    let clips = ["One", "Two", "Three", "Four"]
    private var token: NSKeyValueObservation?

    override func viewDidAppear() {
        super.viewDidAppear()
        addAllVideosToPlayer()
    }

    func addAllVideosToPlayer() {
        avPlayerView.player = player
        for clip in clips {
            let urlPath = Bundle.main.path(forResource: clip, ofType: "mov")!
            let url = URL(fileURLWithPath: urlPath)
            let playerItem = AVPlayerItem(url: url)
            player.insert(playerItem, after: player.items().last)

            token = player.observe(\.currentItem) { [weak self] player, _ in
                if self!.player.items().count == 1 { self?.addAllVideosToPlayer() }
            }
            player.play()
            avPlayerView.controlsStyle = .none
        }
    }

    @IBAction func nextVideo(_ sender: Any) {
        var number: Int = 0
        let clip = clips[number]

        let urlPath = Bundle.main.path(forResource: clip, ofType: "mov")!
        let url = URL(fileURLWithPath: urlPath)
        let playerItem = AVPlayerItem(url: url)
        player.insert(playerItem, after: player.items().last)

        player.advanceToNextItem()
        number += 1

        if number >= clips.count {
            number = 0
            player.advanceToNextItem()
            number += 1
        }
    }
}

Here's my ViewController:

enter image description here


Solution

  • You always access the first video here:

      var number: Int = 0
      let clip = clips[number]
    

    number is always zero. Keep it as a member of the class and update it where needed.