使用 AVAssetDownloadURLSession 正确监控离线下载的进度

7
我正在使用AVAssetDownloadURLSession和AVAssetDownloadDelegate来下载iOS 11离线视频,使用makeAssetDownloadTask(...)和aggregateAssetDownloadTask(...)方法。一切正常,但我想让用户尽可能准确地了解每个下载的进度。为此,我使用:
- urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange)

- urlSession(_ session: URLSession, aggregateAssetDownloadTask: AVAggregateAssetDownloadTask, didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange, for mediaSelection: AVMediaSelection)

通过这些方法,我能够单独计算视频和每个音轨/字幕的下载进度。因此,我认为视频下载占总进度的70%,而音轨/字幕下载占30%,并根据其数量进行划分。 我将每个项目的下载进度存储在临时字典中(乘以它们的比率),并将每个下载进度添加到全局表示中。
最终它运作良好,但不如预期的那么流畅。例如,如果音轨的权重较轻,则进度会在1秒钟内从70%跳至85%(如果我有2个音轨要下载),然后立即减速。 我希望有一个全局进度。
我对这个解决方案一点也不满意,但目前我没有找到其他解决方案。
你们有什么想法/解决方案吗?
谢谢大家。

我很想看看你的代码是如何实现这个的,我也遇到了同样的问题,你的解决方案比我现在的好。 - Andy Obusek
很遗憾,我还没有找到解决方案,因为我没有足够的时间去寻找更好的方法来解决它。 - Guillaume
完全相同的问题:( 很遗憾他们没有为此添加全局进度。@Guillaume,您能否分享您的计算作为参考? - mgyky
@mgyky,实际上,我已经离开了公司,并选择使用另一家不开源的公司的SDK,所以很抱歉我不能告诉你。但在此之前,我做的是一个近似计算。我有1个视频文件和4个轨道。因此,我进行了一个计算,视频=80%,4个轨道=20%。它可以正确地工作,但对于某些下载,由于视频的大小和类型以及轨道的大小,可能会非常快速地结束。 - Guillaume
@Guillaume 谢谢你的快速回答 :) 实际上,我看了 WWDC 2020 的发现如何下载并离线播放 HLS 会话,并且演示者说了完全相同的话。无论如何,在开始编写代码之前,我想看一个例子 :) 我的轨道是动态的,有时有2个声音,2个字幕,有时只有1个声音,就这样。我会理解的。谢谢你,并祝你在新职位中一切顺利。 - mgyky
@Guillaume,我一段时间前分享了我的解决方案,如果你觉得有用,请标记为正确答案。 - mgyky
1个回答

1

解决方案

首先,这只是我的情况,您应该根据自己的需求更新方法。

在我的场景中,作为一个最复杂的数组示例,我有类似于这样的东西:

[English(soun),Turkish(soun),English(sbtl),Turkish(sbtl)]

我们会为所有音频提供80%的内容,并为所有字幕提供20%的内容。
总计算公式必须类似于这样:
(100*(0,80/2))+(100*(0,80/2))+(100*(0,20/2))+(100(0,20/2))

我们需要一个枚举类型来表示AVMediaType

// AVMediaType
enum MediaType: String {
    // Sound
    case soun
        
    // Subtitle
    case sbtl
      
    // Closed Caption
    // case clcp // Removed for simplifying the example.
}

此外,我们需要一个字典来处理资产下载进度:
var totalDownloadProgressForAsset = [String: Double]()

这里是方法:

public func getTotalProgress() -> Double {
    var result = 0.0
    let soundMultiplier          = 0.80
    let subtitleMultiplier       = 0.20
    var soundCount = 0
    var subtitleCount  = 0
    
    for (key, _) in totalDownloadProgressForAsset {
        let mediaTypeKey = String(key.suffix(4))
        let type = MediaType(rawValue: mediaTypeKey)
        
        switch type {
        case .soun:
            soundCount += 1
        case .sbtl:
            subtitleCount += 1
        case .none:
            break
        }
    }
                    
    for (key, value) in totalDownloadProgressForAsset {
        let mediaTypeKey = String(key.suffix(4))
        let type = MediaType(rawValue: mediaTypeKey)
        
        switch type {
        case .soun:
            result += Double(value*(soundMultiplier/Double(soundCount)))
        case .sbtl:
            result += Double(value*(subtitleMultiplier/Double(subtitleCount)))
        case .none:
            break
        }
    }
    
    return result
}

当然,我们可以以不同的方式编写这个例子,这只是为了举例。

随时提出任何关于它的问题。


嗨mgykj,你如何确定所有媒体选择中的AVMediaSelection的类型?我也有多个音频和字幕需要下载,但我无法确定每个的类型,因此无法正确地将值相乘。谢谢。 - Arijan
嗨Arijan,你可以使用let type = MediaType(rawValue: mediaTypeKey)来确定类型,然后你可以用switch type分离每个类型。获取媒体类型(如视频、音频、字幕等)的字符串表示形式。请查看我的答案中的switch块。 - mgyky
你好,我目前正在开发一个视频下载功能,并尝试在多个MediaSelection下载中更新进度。我原以为第一个要下载的轨道应该是MediaVideo类型的轨道,但似乎我只下载音频和字幕,你也是这样吗?因为我看到你的代码示例中只考虑了音频和字幕。 - Saliom
嗨mgyky,我正在使用AVAggregateAssetDownloadTask,从urlSession:aggregateAssetDownloadTask:didLoad:totalTimeRangesLoaded:timeRangeExpectedToLoad:for中获取“mediaSelection”,但我无法区分此委托被调用的媒体类型(mediaTypeKey)是什么,你是如何解决这个问题的? - Petr Fiala
如果你没有,你需要一个全局的activeDownloadsMap来跟踪任务,否则你不能确定。/// AVAggregateAssetDownloadTask到其对应资产的内部映射。 fileprivate var activeDownloadsMap = AVAggregateAssetDownloadTask: Asset - mgyky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接