AVAssetTrack的资产属性有时为零

时间:2019-04-16 14:05:40

标签: ios asynchronous avfoundation avasset

我正在尝试从asset对象获取AVAssetTrack属性,但有时是nil。看来只有在我使用Dispatch.main.async之后,问题才会出现。

根据documentation,有必要使用loadValuesAsynchronously(forKeys:, completion:)以避免阻塞主线程,并在加载完成后返回主线程。

let asset = AVURLAsset(url: videoInAppBundleURL)
let track = asset.tracks(withMediaType: .video).first!
assert(track.asset != nil) // passes
track.loadValuesAsynchronously(forKeys: [#keyPath(AVAssetTrack.asset)]) {
    assert(track.asset != nil) // passes
    DispatchQueue.main.async {
        assert(track.asset != nil) // FAILS
        // [...]
    }
}

我发现的是:

  • 无论我是在设备上运行还是在 模拟器。
  • 视频/ videoURL似乎没有问题。 该视频是主捆绑包的一部分,我同时尝试了.mp4和.mov 文件,然后通过显示 AVPlayerViewController。

这里是工作中的demo project

我还想知道:为什么AVAssetTrack的{​​{1}}属性是可选的? (所有!!其他属性都是非可选的)

注意:在阅读了Matt的有用评论并进行了进一步调查后,已对这个问题进行了编辑。

1 个答案:

答案 0 :(得分:1)

我通过对您的github示例进行了一些调整来复制该问题,如下所示:

    let asset = AVURLAsset(url: videoInAppBundleURL)
    let tracksKey = #keyPath(AVAsset.tracks)
    asset.loadValuesAsynchronously(forKeys: [tracksKey]) {
        let track = asset.tracks(withMediaType: .video).first!
        DispatchQueue.main.async {
            assert(track.asset != nil) // fails
        }
    }

好的,但是现在当我执行惊人的技巧时,请密切注意:

    let asset = AVURLAsset(url: videoInAppBundleURL)
    let tracksKey = #keyPath(AVAsset.tracks)
    asset.loadValuesAsynchronously(forKeys: [tracksKey]) {
        let track = asset.tracks(withMediaType: .video).first!
        DispatchQueue.main.async {
            print(asset) // <-- amazing trick
            assert(track.asset != nil) // passes!
        }
    }

哇!我所做的只是添加一个print语句-现在突然之间,同样的断言通过了。实际上,这与您最初的声明(您后来将其删除)相平行,即“当使用调试器逐步执行代码时,问题有时消失了。”

所以,现在,我的疑虑被彻底激起,我做了一件令人难以置信的聪明事(即使我自己也这么说)。我删除了print(asset),但是将方案的配置从“调试”切换为“发布”。 Presto,断言仍然通过。

那么您发现的是编译器的一个怪癖-敢称它为bug吗?

但是,等等,还有更多。您很合理地问为什么asset是可选的。这是因为它是weak

weak open var asset: AVAsset? { get }

所以有您的答案。这条赛道对其资产的引用很少。如果我们将跟踪传递到异步队列中,并且我们没有将资产本身与我们一起携带,那么弱引用就会放开,资产会丢失-在调试版本中。

希望这会有所帮助。您可能正在等待我就此是否构成bug做出重大结论,但抱歉,我不会。我提供了两种解决方法(使用Release版本,或故意将资产引用传递到异步队列中),这是我所能做到的。