简体   繁体   English

AVPlayer流媒体进度

[英]AVPlayer streaming progress

I'm successfully using AVPlayer to stream audio from a server and what I want to do now is to show a custom UISlider who shows the progress of the buffering. 我已成功使用AVPlayer从服务器流式传输音频,现在我想要做的是显示一个自定义UISlider ,该UISlider显示缓冲的进度。

Something like this: 像这样:

在此处输入图片说明

With AVPlayer there doesn't seem to be a way to get the total download size or the current downloaded amount for the audio file, only the current playing time and total play time. 使用AVPlayer似乎无法获得音频文件的总下载大小或当前下载量,只有当前播放时间和总播放时间。

There's any workarounds for this? 有什么解决方法吗?

I am just working on this, and so far have the following: 我只是在做这个,到目前为止有以下内容:

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

It should work well: 它应该工作良好:

Objective-C: 目标C:

- (CMTime)availableDuration
{
    NSValue *range = self.player.currentItem.loadedTimeRanges.firstObject;
    if (range != nil){
        return CMTimeRangeGetEnd(range.CMTimeRangeValue);
    }
    return kCMTimeZero;
}

Swift version: 迅捷版:

func availableDuration() -> CMTime
{
    if let range = self.player?.currentItem?.loadedTimeRanges.first {
        return CMTimeRangeGetEnd(range.timeRangeValue)
    }
    return .zero
}

To watch current time value you can use: CMTimeShow([self availableDuration]); 要查看当前时间值,可以使用: CMTimeShow([self availableDuration]); or CMTimeShow(availableDuration()) (for swift) CMTimeShow(availableDuration()) (用于swift)

Personally I do not agree that the timeRanges value will always have a count of 1. 我个人不同意timeRanges值始终为1。

According to the documentation 根据文档

The array contains NSValue objects containing a CMTimeRange value indicating the times ranges for which the player item has media data readily available. 该数组包含NSValue对象,该对象包含CMTimeRange值,该值指示播放器项具有易于获得的媒体数据的时间范围。 The time ranges returned may be discontinuous. 返回的时间范围可能是不连续的。

So this may have values similar to: 因此,其值可能类似于:

[(start1, end1), (start2, end2)] [(start1,end1),(start2,end2)]

From my experience with the hls.js framework within the desktop web world, the holes between these time ranges could be very small or large depending on a multitude of factors, ex: seeking, discontinuities, etc. 根据我在桌面网络世界中使用hls.js框架的经验,这些时间范围之间的漏洞可能非常小,也可能很大,这取决于多种因素,例如:搜索,不连续性等。

So to correctly get the total buffer length you would need to loop through the array and get the duration of each item and concat. 因此,要正确获取缓冲区的总长度,您将需要遍历数组并获取每个项目和concat的持续时间。

If you are looking for a buffer value from current play head you would need to filter the time ranges for a start time that's greater than the current time and an end time that's less than current time. 如果要从当前播放头中查找缓冲区值,则需要过滤时间范围,以用于大于当前时间的开始时间和小于当前时间的结束时间。

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
    }

}

This method will return buffer time interval for your UISlider 此方法将为您的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))
    }
}

Selected answer may cause you problems if returned array is empty. 如果返回的数组为空,则选择的答案可能会导致您出现问题。 Here's a fixed function: 这是一个固定功能:

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

The code from Suresh Kansujiya in Objective C 来自目标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));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM