AVURLAsset(url:, options:) 冻结主线程

问题描述 投票:0回答:1

我正在使用 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中尝试了很多不同的事情,但什么也没发生,而且这种情况突然发生,每次都无法重现。

我尝试解决该问题的变体是 -

  1. 使用后台线程 - 无法正常工作
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)
            }
    }
}
  1. 使用 异步加载值 - 无法正常工作
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
       }
    })
}
ios swift multithreading avplayer avqueueplayer
1个回答
0
投票

我也遇到了同样的问题,并花了很多时间来弄清楚发生了什么事。根本原因可能是纯粹的网络条件,然后尝试初始化

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)

在这种情况下,视频只是未加载,您可以通过超时等安全地处理此问题,并且崩溃消失了。

© www.soinside.com 2019 - 2024. All rights reserved.