我已经成功使用 AVPlayer
从服务器流式传输音频,现在我想做的是显示一个自定义的 UISlider
,以显示缓冲进度。
就像这样:
使用 AVPlayer
没有办法获取音频文件的总下载大小或当前已下载的数量,只能获取当前播放时间和总播放时间。
是否有任何解决方法?
我已经成功使用 AVPlayer
从服务器流式传输音频,现在我想做的是显示一个自定义的 UISlider
,以显示缓冲进度。
就像这样:
使用 AVPlayer
没有办法获取音频文件的总下载大小或当前已下载的数量,只能获取当前播放时间和总播放时间。
是否有任何解决方法?
我正在处理这个问题,目前的进展如下:
- (NSTimeInterval) availableDuration;
{
NSArray *loadedTimeRanges = [[self.player currentItem] loadedTimeRanges];
CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue];
Float64 startSeconds = CMTimeGetSeconds(timeRange.start);
Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;
return result;
}
应该可以正常运行:
Objective-C:
- (CMTime)availableDuration
{
NSValue *range = self.player.currentItem.loadedTimeRanges.firstObject;
if (range != nil){
return CMTimeRangeGetEnd(range.CMTimeRangeValue);
}
return kCMTimeZero;
}
Swift版本:
func availableDuration() -> CMTime
{
if let range = self.player?.currentItem?.loadedTimeRanges.first {
return CMTimeRangeGetEnd(range.timeRangeValue)
}
return .zero
}
要查看当前时间值,您可以使用: CMTimeShow([self availableDuration]); 或 CMTimeShow(availableDuration()) (适用于Swift)
个人认为timeRanges值不一定总是1。
根据文档:
该数组包含NSValue对象,其中包含CMTimeRange值,指示播放器项目具有媒体数据可用的时间范围。返回的时间范围可能是不连续的。
因此,它可能具有类似于以下值:
[(start1, end1), (start2, end2)]
从我在桌面Web世界中使用hls.js框架的经验来看,这些时间范围之间的空隙可能会很小或很大,具体取决于多种因素,例如:寻求、不连续性等。
因此,要正确获取总缓冲区长度,您需要遍历数组并获取每个项的持续时间并连接起来。
如果您正在寻找当前播放头的缓冲区值,则需要过滤开始时间大于当前时间和结束时间小于当前时间的时间范围。
public extension AVPlayerItem {
public func totalBuffer() -> Double {
return self.loadedTimeRanges
.map({ $0.timeRangeValue })
.reduce(0, { acc, cur in
return acc + CMTimeGetSeconds(cur.start) + CMTimeGetSeconds(cur.duration)
})
}
public func currentBuffer() -> Double {
let currentTime = self.currentTime()
guard let timeRange = self.loadedTimeRanges.map({ $0.timeRangeValue })
.first(where: { $0.containsTime(currentTime) }) else { return -1 }
return CMTimeGetSeconds(timeRange.end) - currentTime.seconds
}
}
这个方法将返回您的UISlider的缓冲时间间隔
public var bufferAvail: NSTimeInterval {
// Check if there is a player instance
if ((player.currentItem) != nil) {
// Get current AVPlayerItem
var item: AVPlayerItem = player.currentItem
if (item.status == AVPlayerItemStatus.ReadyToPlay) {
var timeRangeArray: NSArray = item.loadedTimeRanges
var aTimeRange: CMTimeRange = timeRangeArray.objectAtIndex(0).CMTimeRangeValue
var startTime = CMTimeGetSeconds(aTimeRange.start)
var loadedDuration = CMTimeGetSeconds(aTimeRange.duration)
return (NSTimeInterval)(startTime + loadedDuration);
}
else {
return(CMTimeGetSeconds(kCMTimeInvalid))
}
}
else {
return(CMTimeGetSeconds(kCMTimeInvalid))
}
}
如果返回的数组为空,所选答案可能会导致问题。这是一个修复后的函数:
- (NSTimeInterval) availableDuration
{
NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges];
if ([loadedTimeRanges count])
{
CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue];
Float64 startSeconds = CMTimeGetSeconds(timeRange.start);
Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;
return result;
}
return 0;
}
Objective C中Suresh Kansujiya的代码
NSTimeInterval bufferAvail;
if (player.currentItem != nil) {
AVPlayerItem *item = player.currentItem;
if (item.status == AVPlayerStatusReadyToPlay) {
NSArray *timeRangeArray = item.loadedTimeRanges;
CMTimeRange aTimeRange = [[timeRangeArray objectAtIndex:0] CMTimeRangeValue];
Float64 startTime = CMTimeGetSeconds(aTimeRange.start);
Float64 loadedDuration = CMTimeGetSeconds(aTimeRange.duration);
bufferAvail = startTime + loadedDuration;
NSLog(@"%@ - %f", [self class], bufferAvail);
} else {
NSLog(@"%@ - %f", [self class], CMTimeGetSeconds(kCMTimeInvalid)); }
}
else {
NSLog(@"%@ - %f", [self class], CMTimeGetSeconds(kCMTimeInvalid));
}