16

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?

voromax
  • 3,369
  • 2
  • 30
  • 53
Pedro
  • 487
  • 1
  • 5
  • 18
  • i am also having the same issue . did you figure it out for this ? how did you do it ? even i have tried everything . so help me out with this issue . – Moxarth Sep 20 '17 at 13:43
  • I also have the same issue. Did you find the solution? Please share it with us. – Ankur JAIN Feb 21 '18 at 06:40

6 Answers6

17

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:

  1. Wait until the status of the AVPlayerItem is AVPlayerItemStatusReadyToPlay.

  2. 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.

voromax
  • 3,369
  • 2
  • 30
  • 53
7

For swift:

if player.currentItem.status == .readyToPlay {

    print(currentItem.duration.seconds) // it't not nan

}
Au Room
  • 133
  • 1
  • 3
5

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.

algrid
  • 5,600
  • 3
  • 34
  • 37
2

@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)
    }
})
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
2

You can use asset property. It will give you the duration.

self.player.currentItem?.asset.duration.seconds ?? 0
Muhammadjon
  • 190
  • 2
  • 11
1

I had the same problem but I was able to get the duration with a different method. Please see my answer here: https://stackoverflow.com/a/38406295/3629481

Community
  • 1
  • 1
inga
  • 3,154
  • 1
  • 25
  • 29