I have about 200 audio assets about 5 seconds each that are loaded into an AVQueuePlayer
. I do this so it plays all the assets as one full song, but allowing the user to view or jump to a specific verse.
The issue is the main thread gets locked when I load them in like this:
let items = urls
.map(AVAsset.init)
.map(AVPlayerItem.init)
let player = AVQueuePlayer(items: items)
Then I discovered I must asynchronously load them into the player so tried something like this:
let items = urls
.map(AVAsset.init)
.map(AVPlayerItem.init)
let player = AVQueuePlayer()
// Below does NOT compile!!
await withThrowingTaskGroup(of: Void.self) { group in
for var item in items {
group.addTask { _ = try await item.asset.load(.duration) }
for try await result in group {
player?.insert(item, after: nil)
}
}
}
I'm only supporting iOS 16+ so I'm trying to make use of the new Swift Concurrency APIs available in AVFoundation
. I tried following this document but there's a lot of gaps I can't quite get. What is the proper way of loading up that many assets into the queue player without locking up the main thread?
Since it seems that you are trying to directly affect items
and player
I would use actor
. To keep everything together and synchronized.
To bypass captured var 'item' in concurrently-executing code
you can use .enumerated()
actor PlayerLoader{
let items = [URL(string: "https://v.ftcdn.net/01/53/82/41/700_F_153824165_dN7n9QftImnClb7z1IJIjbLgLlkHyYDS_ST.mp4")!]
.map(AVAsset.init)
.map(AVPlayerItem.init)
let player = AVQueuePlayer()
func loadItems() async throws{
try await withThrowingTaskGroup(of: Void.self) { group in
for (idx, _) in items.enumerated() {
group.addTask { [weak self] in
_ = try await self?.items[idx].asset.load(.duration)
}
for try await _ in group {
player.insert(items[idx], after: nil)
}
}
}
}
}
Then just call loadItems
with Task
or Task.detached
let loader = PlayerLoader()
let task = Task{ //or Task.detached depending on where you will be calling this line of code
do{
try await loader.loadItems()
print("total number of items loaded \(loader.player.items().count)")
}catch{
print(error)
throw error
}
}