HLS AVPlayer on iOS - return to live
Asked Answered
P

7

7

I have an AVPlayer which is streaming a live HLS stream.

When the user multitasks the app, I see the play rate drop to 0.0 (paused), when the user comes back it return to 1.0(play), but starts playing from the point it was paused.

What is the best way to force the player back to live without restarting the stream completely? Is there a seekToTime method that handles a closest to live time parameter?

Thanks!

Plausible answered 30/10, 2012 at 18:4 Comment(0)
P
8

I use:

 double time = MAXFLOAT;
 [player seekToTime: CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];

Works well in my app.

Publia answered 15/12, 2012 at 13:43 Comment(0)
D
3

Assuming player is an AVPlayer instance:

CMTimeRange seekableRange = [player.currentItem.seekableTimeRanges.lastObject CMTimeRangeValue];
CGFloat seekableStart = CMTimeGetSeconds(seekableRange.start);
CGFloat seekableDuration = CMTimeGetSeconds(seekableRange.duration);
CGFloat livePosition = seekableStart + seekableDuration;

[player seekToTime:CMTimeMake(livePosition, 1)];
Discommon answered 19/11, 2015 at 2:5 Comment(4)
This is the real answer. Worked for me like a charm.Legionnaire
You can also use CMTimeAdd instead of converting to floating pointBabara
You can also use CMTimeRangeGetEnd instead of adding the duration to the start.Arabele
When you wait long enough this solution leads to exceptions because the time is not valid.Dartmoor
S
2

Swift version of Igor Kulagin answer:

player.seek(to: kCMTimePositiveInfinity)
player.play()

Works perfectly in any condition. Other solutions gave me NaN error calculating livePosition value, or {INVALID} error working directly with CMTime.

Syringe answered 9/1, 2018 at 16:9 Comment(3)
on iOS 14 this doesn't seem to work anymore, had to use the seekableTimeRanges solutionAmidst
This works good for me on iOS 14+.Longsufferance
If you wait long time (few minutes), the return to play is long (few minutes). For short stop it works well. (iOS 16)Dartmoor
D
1

Swift 3.0 Version

public func resumeLive() {
    guard let livePosition = player.currentItem?.seekableTimeRanges.last as? CMTimeRange else {
        return
    }
    player.seek(to:CMTimeRangeGetEnd(livePosition))
}
Durr answered 23/3, 2017 at 5:2 Comment(0)
L
0

Swift version of Karim Mourra's answer:

let seekableRanges = player.currentItem!.seekableTimeRanges
guard seekableRanges.count > 0 else {
  return
}

let range = seekableRanges.last!.CMTimeRangeValue
let livePosition = range.start + range.duration

let minus = CMTimeMakeWithSeconds(Float64(timeOffset), Int32(NSEC_PER_SEC))
let time = livePosition - minus

player.seekToTime(time)
Legionnaire answered 29/3, 2016 at 0:36 Comment(3)
guard let lastRange = player.currentItem?.seekableRanges.last else { return; } player.seekToTime(lastRange.end)Arabele
what's "timeoffset"?Crimson
@HongZhou it's a completely optional buffer in seconds. You can just use livePosition and omit the line with timeOffset.Legionnaire
B
0

No need to convert to floating point if you use Apple's CMTimeRange manipulation functions:

NSValue *value = player.currentItem.seekableTimeRanges.lastObject;
if (value) {
    CMTimeRange seekableRange = [value CMTimeRangeValue];
    CMTime latestTime = CMTimeRangeGetEnd(seekableRange);
    [player seekToTime:latestTime];
} else {
    // there are no seekable time ranges
}

Please see @Fabian's comment below.

Babara answered 30/3, 2016 at 18:40 Comment(1)
No need to add time if you use CMTimeRangeGetEndArabele
S
0

Swift 4 version:

if let seekableRange = player.currentItem?.seekableTimeRanges.last?.timeRangeValue {
    let seekableStart = seekableRange.start
    let seekableDuration = seekableRange.duration
    let livePosition = seekableStart + seekableDuration

    player.seek(to: livePosition, completionHandler: { [weak self] _ in
        self?.player.play()
    })
}
Spinach answered 30/4, 2020 at 20:45 Comment(1)
It produces excpetions when you wait long time because time is not valid.Dartmoor

© 2022 - 2024 — McMap. All rights reserved.