我目前正在用FairPlay流实现离线流。为此,我使用AVAssetDownloadTask下载流。
我想给用户关于开始下载的大小的反馈:
您确定要下载此流吗?下载将需要2.4GB,您目前还剩下14 of的空间
我已经检查了像countOfBytesReceived和countOfBytesExpectedToReceive这样的属性,但是这些属性不会返回正确的值。
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
发布于 2016-10-04 18:43:20
HLS流实际上是称为清单和传输流的文件的集合。清单通常包含子清单的文本列表(每个子清单对应不同的比特率),而这些子清单包含包含实际电影数据的传输流列表。
在您的代码中,当您下载HLS URL时,您实际上只下载了主清单,这通常是几千字节。如果要复制整个流,则需要解析所有清单,复制原始流的文件夹结构,并获取传输段(这些片段通常在10秒内,因此可能有数百个这样的片段)。如果清单也使用绝对URL指定,则可能需要重写URL。
要计算每个流的大小,可以将比特率(在主清单中列出)乘以流的持续时间;对于下载而言,这可能是一个足够好的估计。
在这里,更好的答案是实现AVAssetDownloadTask,因为您在离线FairPlay的上下文中使用AVAssetDownloadDelegate。该协议中的一种方法为您提供了您想要的进展:
URLSession:assetDownloadTask:didLoadTimeRange:totalTimeRangesLoaded:timeRangeExpectedToLoad:
下面是显示此委托的WWDC 2016会议504。
有很多与FairPlay的离线播放有关的细节,所以非常仔细地浏览这个视频是个好主意。
发布于 2016-09-20 05:05:07
我没有亲自使用过这个API,但我至少对HTTP流有点熟悉。有了这些知识,我想我知道为什么你不能得到你想要的信息。
HLS协议旨在处理实时流和固定长度资产流.它可以通过将媒体切入大约10秒的块IIRC,并在特定URL的播放列表文件中列出这些块的URL。
如果播放列表没有改变,那么您可以下载播放列表,计算文件数量,获取第一个文件的长度,并将其乘以文件数,您将得到一个粗略的近似,当您开始检索最后一个块时,可以用确切的值替换它。
但是,并不能保证播放列表不会改变。使用HLS的,通过删除最老段(或不删除)并在末尾添加新段,播放列表可能每10秒更改一次。通过这种方式,HLS支持没有结束的直播流。在这种情况下,下载有一个大小的概念是荒谬的。
更糟糕的是,2464可能是播放列表文件的大小,而不是其中第一个资产的大小,也就是说,除非子类的didReceiveResponse:方法工作,否则它不会告诉您任何信息,在这种情况下,您可以通过读取Content-Length头来获取每个片段的长度。即使它正常工作,您可能仍然无法从这个API中获得段数(而且也不能保证所有段的长度完全相同,尽管它们应该非常接近)。
我怀疑,要获得你想要的信息,即使是对于一个非真实资产,你可能需要获取播放列表,自己解析它,并为其中列出的每个媒体段URL执行一系列的头请求。
幸运的是,HLS规范是一个可公开使用的标准,因此,如果您想沿着这条道路走下去,可以阅读一些RFCs来了解播放列表文件的结构。而且AFAIK,播放列表本身不使用任何DRM或任何东西加密,所以应该可以这样做,即使API的实际解密部分不是公开的(AFAIK)。
发布于 2017-10-31 15:21:30
这是我的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;
}https://stackoverflow.com/questions/39571523
复制相似问题