2

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

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220

2 Answers2

2

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.

giorashc
  • 13,691
  • 3
  • 35
  • 71
  • 1
    number is defined locally in the method. After the method returns this variable is released. (not to mention that you assign it to zero every time the method is called). Define it as a member of the class so it will maintain its value after the method returns – giorashc Nov 07 '18 at 16:10
0

Here's the solution of my problem (thanks to @giorashc) :

class ViewController: NSViewController {

    var number: Int = 0                                         // class member

    .........................................
    .........................................

    @IBAction func nextVideo(_ sender: Any) {

        let clip = clips[number % 4]                            // modulus of 4   

        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
    }
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220