AVQueuePlayer can't play sounds more than once

I have an array of AVPlayerItems

var anArrayOfAVPlayerItems = [
    AVPlayerItem(url: Bundle.main.url(forResource: "Cshort", withExtension: "mp3")!),
    AVPlayerItem(url: Bundle.main.url(forResource: "Dshort", withExtension: "mp3")!),
    AVPlayerItem(url: Bundle.main.url(forResource: "Eshort", withExtension: "mp3")!)
]()

and I pass that array into my AVQueuePlayer

var sequentialPlayer: AVQueuePlayer = {
    let queue = AVQueuePlayer(items: anArrayOfAVPlayerItems)
    return queue
}()

When I tap on this button, it plays 3 audio files one after another.

Button(action: {
    sequentialPlayer.play()
}) {
    Text("button")
}

but if I tap on this button again, it won't play anything.

How can I get the AVQueuePlayer to play the entire AVPlayerItem array again from the beginning every time i tap on the button?

The player has state about its current playback progress. Consider calling .play() followed by a pause and then a second .play(). Would you still expect the player to restart, or to continue from where it paused?

You must either seek the player back to the beginning, or recreate the player with empty state, I think.

I can understand your answer but I don't know exactly how to do it. I've tried seeking the player back to the beginning by writing something like this:

Button(action: {
    sequentialPlayer.play()
    sequentialPlayer.seek(to: .zero)
}) {
    Text("button")
}

but it didn't work, tried a few other methods too, none of them worked.

I've also tried creating a new AVQueuePlayer every time I tap on the button. But XCode tells me " An AVPlayerItem cannot be associated with more than one instance of AVPlayer "

I would assume you should seek to the beginning before playing, non?

After the first time you invoke this code, the players seek position is at the end, right?

Then, the second time the user taps the button, you try to play (at the end, will stop immediately), then seek to the beginning.

I haven’t tried, but I would assume the button works every other time you tap it?

no it doesn't work every other time, it only works the first time.
I also called the seek method before the play method,

Button(action: {
    sequentialPlayer.seek(to: .zero)
    sequentialPlayer.play()
}) {
    Text("button")
}

But I still experience the same problem. It only plays successfully the first time, and then the button stops playing sounds no matter how many times I tap it.

Items are removed from the queue once they're finished playing. You may need to put them back in.

sorry, I'm not quite sure how to do that. I put all the items in an array and passed it in the queue like this:

var anArrayOfAVPlayerItems = [
    AVPlayerItem(url: Bundle.main.url(forResource: "Cshort", withExtension: "mp3")!),
    AVPlayerItem(url: Bundle.main.url(forResource: "Dshort", withExtension: "mp3")!),
    AVPlayerItem(url: Bundle.main.url(forResource: "Eshort", withExtension: "mp3")!)
]()

var sequentialPlayer: AVQueuePlayer = {
    let queue = AVQueuePlayer(items: anArrayOfAVPlayerItems)
    return queue
}()

shouldn't the queue always have all the items since I passed an array of items in the queue as an argument?

if all these items get removed from the queue, how do I put them back in? do I repopulate the array?

AVQueuePlayer: Managing the Player Queue

The docs for advanceToNextItem() tells you that going to the next item removes the current item from the queue (you can then extrapolate that this is the same as when one of your item reaches the end). You can use the items function to check what's in the queue after playing if you want to make sure. And you'll find an insert function in there too so you can add items to the queue.

I made some progress after following your answer. Right now the player does play sound every time I tap the button. The first time I tap the button it plays all 3 sound files / items. But if I tap the button again, it would only play the first item in the queue, even though there are 3 items in the queue.

import SwiftUI
import AVFoundation

let item1 = AVPlayerItem(url: Bundle.main.url(forResource: "Cshort", withExtension: "mp3")!)
let item2 = AVPlayerItem(url: Bundle.main.url(forResource: "Dshort", withExtension: "mp3")!)
let item3 = AVPlayerItem(url: Bundle.main.url(forResource: "Eshort", withExtension: "mp3")!)

var itemArray = [item1, item2, item3]

var player: AVQueuePlayer = {
    let queue = AVQueuePlayer(items: itemArray)
    return queue
}()

struct ContentView: View {
    var body: some View {
        Button(action: {
            if player.items().isEmpty {
                print("queue is empty")
                print("about to insert")
                for item in itemArray {
                    if player.items().isEmpty {
                        player.insert(itemArray[0], after: nil)
                    } else {
                        player.insert(item, after: player.items().last)
                        
                    }
                }
                print("inserted these items \(player.items())")
            } else {
                print("queue has items to play, no need to insert")
            }
            
            player.seek(to: .zero)
            print("will play from this item: \(player.currentItem!)")
            print("about to play these items: \(player.items())")
            player.play()
            print("done playing——————————————————")
            
        }){
            Text("Button")
        }
    }
}

If I tap the button for the first time, it plays all 3 items one after another, and this is what gets printed in the console:

queue has items to play, no need to insert

will play from this item: <AVPlayerItem: 0x283a44390, asset = <AVURLAsset: 0x283853ce0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Cshort.mp3>>

about to play these items: [<AVPlayerItem: 0x283a44390, asset = <AVURLAsset: 0x283853ce0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Cshort.mp3>>, <AVPlayerItem: 0x283a44b20, asset = <AVURLAsset: 0x28386ca60, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Dshort.mp3>>, <AVPlayerItem: 0x283a44c60, asset = <AVURLAsset: 0x28386c9c0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Eshort.mp3>>]

done playing——————————————————

And after that, every I tap the button it only plays the first item, and this is what gets printed in the console every time:
queue is empty

about to insert

inserted these items [<AVPlayerItem: 0x283a44390, asset = <AVURLAsset: 0x283853ce0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Cshort.mp3>>, <AVPlayerItem: 0x283a44b20, asset = <AVURLAsset: 0x28386ca60, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Dshort.mp3>>, <AVPlayerItem: 0x283a44c60, asset = <AVURLAsset: 0x28386c9c0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Eshort.mp3>>]

will play from this item: <AVPlayerItem: 0x283a44390, asset = <AVURLAsset: 0x283853ce0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Cshort.mp3>>

about to play these items: [<AVPlayerItem: 0x283a44390, asset = <AVURLAsset: 0x283853ce0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Cshort.mp3>>, <AVPlayerItem: 0x283a44b20, asset = <AVURLAsset: 0x28386ca60, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Dshort.mp3>>, <AVPlayerItem: 0x283a44c60, asset = <AVURLAsset: 0x28386c9c0, URL = file:///private/var/containers/Bundle/Application/F30EC8BB-9C51-42AD-AEF7-840DBAEFCF79/AVQueue.app/Eshort.mp3>>]

done playing——————————————————

Hey, were you able to fix this problem? I'm currently having the same problem and I'm banging my head against the wall trying to fix it.

I fixed it by using my code.

You have to reinitialize avqueueplayer on playing end event. here is the sample of code

func resetAudioPlayer() {
    do {
        try  AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try  AVAudioSession.sharedInstance().setActive(true)
    } catch _ as NSError {
        print("dd")
    }
    var playerItems : [AVPlayerItem] = [];
    for file in myFiles {
        playerItems.append(AVPlayerItem(url: URL(fileURLWithPath: file)));
    }
    avPlayer = AVQueuePlayer(items: playerItems);
    if ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10, minorVersion: 0, patchVersion: 0)) {
        avPlayer.automaticallyWaitsToMinimizeStalling = false
    }
    NotificationCenter.default.addObserver(self, selector: #selector(videoDidEnd), name:
    .AVPlayerItemDidPlayToEndTime, object: avPlayer.items().last)
}

And Here is the function to handle end video event

@objc func videoDidEnd(notification: NSNotification) {
    avPlayer.removeAllItems();
    resetAudioPlayer();
    avPlayer.seek(to: kCMTimeZero);
    isPlayingAudio = false;
}

See my comment above

Terms of Service

Privacy Policy

Cookie Policy