下载前获取AVAssetDownloadTask的大小



我目前正在使用FairPlay流媒体实现离线流媒体。因此,我正在使用AVAssetDownloadTask下载流。

我想给用户关于开始下载的大小的反馈:

您确定要下载此流吗?下载需要2.4GB的空间,而你现在还有14GB的空间

我已经检查了属性,如countOfBytesReceivedcountOfBytesExpectedToReceive,但这些不会返回正确的值。

let headRequest = NSMutableURLRequest(URL: asset.streamURL)
headRequest.HTTPMethod = "HEAD"
let sizeTask = NSURLSession.sharedSession().dataTaskWithRequest(headRequest) { (data, response, error) in
    print("Expected size is (response?.expectedContentLength)")
}.resume()

打印大小为2464,其中最后的大小为3GB。

在下载期间,我记录了上面的属性:

func URLSession(session: NSURLSession, assetDownloadTask: AVAssetDownloadTask, didLoadTimeRange timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange) {
    print("Downloaded ( convertFileSizeToMegabyte(Float(assetDownloadTask.countOfBytesReceived)))/(convertFileSizeToMegabyte(Float(assetDownloadTask.countOfBytesExpectedToReceive))) MB")
}

但这些都是零:

已下载0.0/0.0 MB

HLS流实际上是称为清单和传输流的文件集合。清单通常包含子清单的文本列表(每个子清单对应不同的比特率),并且这些子清单包含包含实际电影数据的传输流列表。

在你的代码中,当你下载HLS URL时,你实际上下载的只是主清单,那通常是几千字节。如果要复制整个流,则需要解析所有清单,复制原始流的文件夹结构,并获取传输段(通常为10秒段,因此可能有数百个)。如果清单也使用绝对url指定,则可能需要重写url。

要计算每个流的大小,您可以将比特率(在主清单中列出)乘以流的持续时间;对于下载的目的,这可能是一个足够好的估计。

一个更好的答案,因为你在离线FairPlay的上下文中使用AVAssetDownloadTask,是实现AVAssetDownloadDelegate。协议中的一个方法提供了你正在寻找的进度:

URLSession: assetDownloadTask: didLoadTimeRange: totalTimeRangesLoaded: timeRangeExpectedToLoad:

这是WWDC 2016年第504次会议,展示了这个委托的作用。

在FairPlay中有很多关于离线播放的细节,所以最好仔细看一下这个视频

我个人没有使用过这个API,但我至少对HTTP Live Streaming有点熟悉。有了这些知识,我想我知道为什么你找不到你想要的信息了。

HLS协议设计用于处理实时流以及固定长度资产的流。它通过将媒体分割成通常约为10秒的块,IIRC,并在特定URL的播放列表文件中列出这些块的URL来实现这一点。

如果播放列表没有改变,那么您可以下载播放列表,计算文件的数量,获得第一个文件的长度,并将其乘以文件的数量,您将得到一个粗略的近似值,当您开始检索最后一个块时,您可以将其替换为一个精确的值。

然而,不能保证播放列表不会改变。使用HLS,播放列表可能每十秒改变一次,通过删除最老的片段(或不)并在最后添加新的片段。通过这种方式,HLS支持没有尽头的直播流。在这种情况下,下载有大小的概念是没有意义的。

更糟的是,2464可能是播放列表文件的大小,而不是其中第一个资产的大小,也就是说,它告诉你什么也没有,除非子类的didReceiveResponse:方法工作,在这种情况下,你可能能够通过读取Content-Length头来获取每个段的长度。即使它正常工作,你可能仍然无法从这个API获得段的数量(也不能保证所有的段都是完全相同的长度,尽管它们应该非常接近)。

我怀疑要获得您想要的信息,即使对于非活动资产,您可能必须获取播放列表,自己解析它,并对其中列出的每个媒体段url执行一系列HEAD请求。

幸运的是,HLS规范是一个公开可用的标准,所以如果你想沿着这条路走下去,你可以阅读rfc来了解播放列表文件的结构。AFAIK,播放列表本身没有加密任何DRM或任何东西,所以它应该可以这样做,即使API的实际解密部分不是公开的(AFAIK)。

这是我计算最终下载大小的c#/Xamarin代码。它很可能不完美,尤其是在iOS11支持的新编解码器的情况下,但你应该明白。

private static async Task<long> GetFullVideoBitrate(string manifestUrl)
{
    string bandwidthPattern = "#EXT-X-STREAM-INF:.*(BANDWIDTH=(?<bitrate>\d+)).*";
    string videoPattern = "^" + bandwidthPattern + "(RESOLUTION=(?<width>\d+)x(?<height>\d+)).*CODECS=".*avc1.*".*$";
    string audioPattern = "^(?!.*RESOLUTION)" + bandwidthPattern + "CODECS=".*mp4a.*".*$";
    HttpClient manifestClient = new HttpClient();
    Regex videoInfoRegex = new Regex(videoPattern, RegexOptions.Multiline);
    Regex audioInfoRegex = new Regex(audioPattern, RegexOptions.Multiline);
    string manifestData = await manifestClient.GetStringAsync(manifestUrl);
    MatchCollection videoMatches = videoInfoRegex.Matches(manifestData);
    MatchCollection audioMatches = audioInfoRegex.Matches(manifestData);
    List<long> videoBitrates = new List<long>();
    List<long> audioBitrates = new List<long>();
    foreach (Match match in videoMatches)
    {
        long bitrate;
        if (long.TryParse(match.Groups["bitrate"]
                               .Value,
                          out bitrate))
        {
            videoBitrates.Add(bitrate);
        }
    }
    foreach (Match match in audioMatches)
    {
        long bitrate;
        if (long.TryParse(match.Groups["bitrate"]
                               .Value,
                          out bitrate))
        {
            audioBitrates.Add(bitrate);
        }
    }
    if (videoBitrates.Any() && audioBitrates.Any())
    {
        IEnumerable<long> availableBitrate = videoBitrates.Where(b => b >= Settings.VideoQuality.ToBitRate());
        long videoBitrateSelected = availableBitrate.Any() ? availableBitrate.First() : videoBitrates.Max();
        long totalAudioBitrate = audioBitrates.Sum();
        return videoBitrateSelected + totalAudioBitrate;
    }
    return 0;
}

相关内容

  • 没有找到相关文章

最新更新