我有大约 200 个音频资源,每个资源大约 5 秒,已加载到 AVQueuePlayer
中。我这样做是为了将所有资源作为一首完整的歌曲播放,但允许用户查看或跳转到特定的诗句。
问题是当我像这样加载它们时主线程被锁定:
let items = urls
.map(AVAsset.init)
.map(AVPlayerItem.init)
let player = AVQueuePlayer(items: items)
然后我发现我必须将它们异步加载到播放器中,所以尝试了这样的事情:
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)
}
}
}
我仅支持 iOS 16+,因此我尝试使用 AVFoundation
中提供的新 Swift Concurrency API。我尝试遵循 this document但还有很多我无法完全理解的差距。在不锁定主线程的情况下将这么多资源加载到队列播放器中的正确方法是什么?
最佳答案
因为您似乎试图直接影响items
和player
,所以我会使用actor
。让一切保持在一起并同步。
要绕过在并发执行的代码中捕获的var“item”
,您可以使用.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)
}
}
}
}
}
然后只需使用 Task
或 Task.detached
调用 loadItems
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
}
}
关于ios - 如何使用Swift Concurrency加载AVQueuePlayer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74748934/