Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 7 AVPlayer AVPlayerItem duration incorrect in iOS 7

I have the following code in my app:

NSURL *url = [NSURL fileURLWithPath: [self.DocDir stringByAppendingPathComponent: self.FileName] isDirectory: NO];
self.avPlayer = [AVPlayer playerWithURL: url];

Float64 duration = CMTimeGetSeconds(self.avPlayer.currentItem.duration);

This worked fine with iOS 6 but with iOS 7 for some reason it returns NaN. When inspecting self.avPlayer.currentItem.duration the CMTime object has 0's with a flag of 17.

Interestingly the player works fine, just the duration is wrong.

Has anyone else experienced the same issues? I am importing the following:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CoreMedia/CoreMedia.h>
#import <AVFoundation/AVAsset.h>
like image 250
Matt Wolfe Avatar asked Sep 27 '13 20:09

Matt Wolfe


3 Answers

After playing around with different ways of initializing the objects I arrived at a working solution:

AVURLAsset *asset = [AVURLAsset assetWithURL: url];
Float64 duration = CMTimeGetSeconds(asset.duration);
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset: asset]; 
self.avPlayer = [[AVPlayer alloc] initWithPlayerItem: item];

It appears the duration value isn't always immediately available from an AVPlayerItem but it seems to work fine with an AVAsset immediately.

like image 123
Matt Wolfe Avatar answered Oct 26 '22 17:10

Matt Wolfe


In iOS 7, for AVPlayerItem already created, you can also get duration from the underlaying asset:

CMTimeGetSeconds([[[[self player] currentItem] asset] duration]);

Instead of get it directly from AVPlayerItem, which gives you a NaN:

CMTimeGetSeconds([[[self player] currentItem] duration]);
like image 40
Derek Li Avatar answered Oct 26 '22 16:10

Derek Li


The recommended way of doing this, as described in the manual is by observing the player item status:

[self.avPlayer.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial context:nil];

Then, inside observeValueForKeyPath:ofObject:change:context:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // TODO: use either keyPath or context to differentiate between value changes
    if (self.avPlayer.currentItem.status == AVPlayerStatusReadyToPlay) {
        Float64 duration = CMTimeGetSeconds(self.avPlayer.currentItem.duration);
        // ...
    }
}

Also, make sure that you remove the observer when you change the player item:

if (self.avPlayer.currentItem) {
    [self.avPlayer.currentItem removeObserver:self forKeyPath:@"status"];
}

Btw, you can also observe the duration property directly; however, it's been my personal experience that the results aren't as reliable as they should be ;-)

like image 6
Ja͢ck Avatar answered Oct 26 '22 16:10

Ja͢ck