我正在使用 AVQueuePlayer 在我的应用程序中播放队列中的项目。因此,当假设有 10 个项目要添加到队列中时,我只需使用 for 循环将它们添加到队列中。大多数时候它工作正常,但有时它会冻结整个应用程序。 线程总是挂在这里let asset = AVURLAsset(url: url),然后冻结
这是我的代码-
func loadAssets() {
let URLS = ["http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4","http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4","http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4","http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4"]
for urlString in URLS {
let contentURL = URL(string: urlString)!
self.createAVAsset(url: contentUrl, completion: ({ [weak self] asset in
guard let self = self else { return }
let playerItem = self.createPlayerItemForAsset(asset)
self.queuePlayer?.insert(playerItem, after: nil)
}))
}
}
func createAVAsset(url: URL, completion: @escaping ((AVURLAsset) -> Void)) {
let asset = AVURLAsset(url: url) /*THREAD HANGS ON THIS LINE*/
asset.resourceLoader.setDelegate(self, queue: self.queue)
completion(asset)
}
我在方法createAVAsset中尝试了很多不同的事情,但什么也没发生,而且这种情况突然发生,每次都无法重现。
我尝试解决该问题的变体是 -
func createAVAsset(url: URL, completion: @escaping ((AVURLAsset) -> Void)) {
DispatchQueue.global(qos: .background).async { [weak self] in
guard let self = self else { return }
let asset = AVURLAsset(url: url) /*THREAD HANGS ON THIS LINE*/
asset.resourceLoader.setDelegate(self, queue: self.queue)
DispatchQueue.main.async {
completion(asset)
}
}
}
func createAVAsset(url: URL, completion: @escaping ((AVURLAsset) -> Void)) {
let asset = AVURLAsset(url: url) /*THREAD HANGS ON THIS LINE*/
let keys: [String] = ["playable"]
//Control doesn't reach below, it hangs on above line already
asset.loadValuesAsynchronously(forKeys: keys, completionHandler: {
var error: NSError? = nil
let status = asset.statusOfValue(forKey: "playable", error: &error)
switch status {
case .loaded:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
completion(asset)
}
break
case .failed:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
}
break
case .cancelled:
DispatchQueue.main.async {
//do something, show alert, put a placeholder image etc.
}
break
default:
break
}
})
}
我也遇到了同样的问题,并花了很多时间来弄清楚发生了什么事。根本原因可能是纯粹的网络条件,然后尝试初始化
AVURLAsset(url: url)
会阻塞主线程:
Thread 0 - FigSandboxRemoteClientQueue - (TH_STATE_WAITING)
0 libsystem_kernel.dylib +0x1804 _mach_msg2_trap
1 libsystem_kernel.dylib +0x5004 _mach_msg2_internal
2 libsystem_kernel.dylib +0x4f1c _mach_msg_overwrite
3 libsystem_kernel.dylib +0x4d5c _mach_msg
4 libdispatch.dylib +0x1eb14 __dispatch_mach_send_and_wait_for_reply
5 libdispatch.dylib +0x1eeb4 _dispatch_mach_send_with_result_and_wait_for_reply
6 libxpc.dylib +0x10ac4 _xpc_connection_send_message_with_reply_sync
7 CoreMedia +0x129988 _FigXPCConnectionSendSyncMessageCreatingReply
8 CoreMedia +0x1293d4 _FigXPCRemoteClientSendSyncMessageCreatingReply
9 CoreMedia +0x4b860 _FigSandboxRegistrationRemoteCreate
10 CoreMedia +0x1139b4 ___fsbxpc_sandboxRegisterURLWithProcessCommon_block_invoke
11 libdispatch.dylib +0x3dd0 __dispatch_client_callout
12 libdispatch.dylib +0x132c0 __dispatch_lane_barrier_sync_invoke_and_complete
13 CoreMedia +0x1133cc _fsbxpc_sandboxRegisterURLWithProcessCommon
14 MediaToolbox +0x645a70 _remoteXPCAsset_CreateInternal
15 MediaToolbox +0x645514 _FigAssetRemoteCreateWithURL
16 MediaToolbox +0x645f40 _FigAssetRemoteCreateWithURLAndRetry
17 AVFCore +0x16c98 -[AVFigAssetInspectorLoader initWithURL:figAssetCreationFlags:figAssetCreationOptions:avAssetInitializationOptions:forAsset:figErr:]
18 AVFCore +0x2b090 _avurlasset_setupGuts
19 AVFCore +0xf8ec -[AVURLAsset initWithURL:options:]
20 AVFCore +0x38a38 +[AVAsset assetWithURL:]
我使用崩溃分析工具在各种设备和 iOS 版本上观察到了这种行为。最终我决定将
AVURLAsset
创建放到后台以避免阻塞主线程,例如:
class Player: NSObject {
let queuePlayer = AVQueuePlayer()
private func createAVAsset(url: URL) async -> AVURLAsset {
let asset = AVURLAsset(url: url)
asset.resourceLoader.setDelegate(self, queue: .global())
return asset
}
init(url: URL) {
super.init()
Task { @MainActor in
let asset = await createAVAsset(url: url)
let playerItem = AVPlayerItem(asset: asset)
queuePlayer.insert(playerItem, after: nil)
}
}
}
extension Player: AVAssetResourceLoaderDelegate {
}
let url = URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!
let player = Player(url: url)
在这种情况下,视频只是未加载,您可以通过超时等安全地处理此问题,并且崩溃消失了。