문제

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