AVPlayer流媒体进度

37

我已经成功使用 AVPlayer 从服务器流式传输音频,现在我想做的是显示一个自定义的 UISlider,以显示缓冲进度。

就像这样:

enter image description here

使用 AVPlayer 没有办法获取音频文件的总下载大小或当前已下载的数量,只能获取当前播放时间和总播放时间。

是否有任何解决方法?


3
你有沒有實現這個UI部分?我需要確切的東西,如果已經有東西存在,我寧願不自己做。 - Ben Scheirman
3
请查看该问题的最佳答案,其中涉及UI部分:https://dev59.com/rm855IYBdhLWcg3wKxDc - Form
1
实现上述UI的简单解决方案是,在UISlider下方放置一个UIProgressBar,并将滑块的"maximumTrackTintColor"设置为"[UIColor clearColor]"。 - inorganik
请参考这个答案:https://dev59.com/1m855IYBdhLWcg3wp2J0#39036307 这可能对你有所帮助。 - iPatel
6个回答

58

我正在处理这个问题,目前的进展如下:

- (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;
}

为什么你要取第一次,而不是最后一次?[loadedTimeRanges lastObject] - HotJard
我记得我写了一个函数来观察它的行为,总是只看到数组中的一个元素,所以选择第一个或最后一个可能是无意义/随意的。在理论上更"正确"的计算方法是使用这个数组,并根据最大开始时间和持续时间来找到最大持续时间,但在实践中不是必要的。 - Andrew Kuklewicz
8
请仔细检查数组是否非空。在 iOS 8 上,它有时会返回一个空数组,导致上述代码崩溃。 - Joris Weimar
这段代码是不是总是返回歌曲时长的相同值?就好像资产已经立即完全缓冲一样。 - Raphael Royer-Rivard
我应该把它放在哪里?我不明白。 - Genevios
显示剩余2条评论

12

应该可以正常运行:

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)


8

个人认为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
    }

}

0

这个方法将返回您的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))
    }
}

你能否详细解释一下这段代码的作用以及如何解决问题?目前的格式有些难以阅读。 - Draken

0

如果返回的数组为空,所选答案可能会导致问题。这是一个修复后的函数:

- (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;
}

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));
}

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