I'm playing a file. mp3 from url by stream. I'm using AVPlayer and when I am trying to get the total time to build a progress bar, I get whenever time is nan.
NSError *setCategoryError = nil;
if ([ [AVAudioSession sharedInstance] isOtherAudioPlaying]) { // mix sound effects with music already playing
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:&setCategoryError];
} else {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&setCategoryError];
}
if (setCategoryError) {
NSLog(@"Error setting category! %ld", (long)[setCategoryError code]);
}
NSURL *url = [NSURL URLWithString:@"http://..//46698"];
AVPlayer *player = [AVPlayer playerWithURL:url];
songPlayer=player;
[songPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == songPlayer && [keyPath isEqualToString:@"status"]) {
if (songPlayer.status == AVPlayerStatusFailed) {
NSLog(@"AVPlayer Failed");
} else if (songPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(@"AVPlayerStatusReadyToPlay");
[songPlayer play];
[songPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time){
CMTime aux = [songPlayer currentTime];
AVPlayerItem *item=[songPlayer currentItem];
CMTime dur=[item duration];
NSLog(@"%f/%f", CMTimeGetSeconds(aux), CMTimeGetSeconds(dur));
}];
} else if (songPlayer.status == AVPlayerItemStatusUnknown) {
NSLog(@"AVPlayer Unknown");
}
}
}
I've tried everything.
[item duration]; /// Fail
[[item asset] duration]; /// Fail
and nothing work
Anyone know why?
The value of duration
property will be reported as kCMTimeIndefinite
until the duration of the underlying asset has been loaded. There are two ways to ensure that the value of duration is accessed only after it becomes available:
Wait until the status of the AVPlayerItem
is AVPlayerItemStatusReadyToPlay
.
Register for key-value observation of the duration
property, requesting the initial value. If the initial value is reported as kCMTimeIndefinite
, the AVPlayerItem
will notify you of the availability of the item's duration via key-value observing as soon as its value becomes known.
For swift:
if player.currentItem.status == .readyToPlay {
print(currentItem.duration.seconds) // it't not nan
}
I have this problem on iOS 12 (for iOS 13 everything works as expected). Current item's duration is always indefinite. I solve it by using player.currentItem?.asset.duration
. Something like this:
private var currentItemDuration: CMTime? {
if #available(iOS 13.0, *) {
return player?.currentItem?.duration
} else {
return player?.currentItem?.asset.duration
}
}
See this answer for macOS: https://stackoverflow.com/a/52668213/7132300 It looks like it's also valid for iOS 12.
@voromax is correct. I added the asset
to the playerItem
without getting the duration
first and duration was nan
:
let asset = AVAsset(url: videoUrl)
self.playerItem = AVPlayerItem(asset: asset)
When I loaded the asset.loadValuesAsynchronously
first, no more nan
and I got the correct duration
:
let assetKeys = ["playable", "duration"]
let asset = AVAsset(url: url)
asset.loadValuesAsynchronously(forKeys: assetKeys, completionHandler: {
DispatchQueue.main.async { [weak self] in
self?.playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: assetKeys)
}
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With