質問

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>
役に立ちましたか?

解決

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.

他のヒント

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]);

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 ;-)

Swift version

You can get the duration using AVAsset which is AVPlayerItem property:

func getVideoDuration(from player: AVPlayer) -> Double? {
    guard let duration = player.currentItem?.asset.duration else { return nil }
    let durationSeconds = CMTimeGetSeconds(duration)
    return durationSeconds
}

or by creating AVAsset from the scratch:

func getVideoDuration(for videoUrl: URL) -> Double {
    let asset = AVAsset(url: videoUrl)
    let duration = asset.duration
    let durationSeconds = CMTimeGetSeconds(duration)
    return durationSeconds
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top