slowman21
slowman21

Reputation: 2515

How to get Duration from AVPlayer (Not AVAudioPlayer)?

I would like to make a UISlider(scrubber) for my AVPlayer. But since this is not an AVAudioPlayer, it doesn't have a built in duration. Any suggestion on how to create the Slider for fast forward, rewind and progress of the playback?

I read the doc on AVPlayer, it has a built in seekToTime or seekToTime:toleranceBefore:toleranceAfter:. I don't really understand it. Would this be the answer for my slider? AVPlayer also has addPeriodicTimeObserverForInterval:queue:usingBlock:, is this for getting the duration of my track? Can someone give me an example on how to implement this code? I am not a fan of Apple's documentation. It seems very hard to understand.

Upvotes: 51

Views: 49121

Answers (9)

Super Developer
Super Developer

Reputation: 897

Check this One using below you also getting progress

Swift

let duration = asset.duration
let durationTime = CMTimeGetSeconds(duration)

let timeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer!)
let timeSecond = CMTimeGetSeconds(timeStamp)
let totalTime = timeSecond / durationTime

DispatchQueue.main.async {
    //code to execute
    self.progress.progress = Float(totalTime)
}

Objective C

CMTime duration = asset.duration;
float durationTime = CMTimeGetSeconds(duration);

CMTime timeStamp = CMSampleBufferGetPresentationTimeStamp( sampleBuffer );
float timeSecond = CMTimeGetSeconds(timeStamp);
float totalTime = timeSecond / durationTime;
                    
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    //code to execute
    [self->progressProcessing setProgress:totalTime animated:YES];
});

Upvotes: 0

Gustavo Conde
Gustavo Conde

Reputation: 977

Although the answers here are correct I believe is valuable to add that you can ask the AVAsset to load the duration (or any other property) and get a callback once it does so you can access the value. This is in response to many commenting that the value of player.currentItem.asset.duration returns 0.00 until the assets gets loaded.

It uses AVAsynchronousKeyValueLoading like this:

let asset = AVURLAsset(url: url)
asset.loadValuesAsynchronously(forKeys: ["duration"]) {
            
    var error: NSError? = nil
    let item: PlaylistItem
            
    // If the duration was loaded, construct a "normal" item,
    // otherwise construct an error item.
    switch asset.statusOfValue(forKey: "duration", error: &error) {
    case .loaded:
        item = PlaylistItem(url: url, title: title, artist: artist, duration: asset.duration)        
    case .failed where error != nil:
        item = PlaylistItem(title: title, artist: artist, error: error!) 
    default:
        let error = NSError(domain: NSCocoaErrorDomain, code: NSFileReadCorruptFileError)
        item = PlaylistItem(title: title, artist: artist, error: error)
    }           
}

This snippet is from Playing Custom Audio with Your Own player sample code by Apple.

Upvotes: 2

Angela Puspitasari
Angela Puspitasari

Reputation: 29

Swift 5

Put this code inside some function that you desired :

let duration = player.currentItem?.duration.seconds ?? 0
let playDuration = formatTime(seconds: duration) //Duration RESULT

Create a function called: formatTime(seconds: Double)

func formatTime(seconds: Double) -> String {
    let result = timeDivider(seconds: seconds)
    let hoursString = "\(result.hours)"
    var minutesString = "\(result.minutes)"
    var secondsString = "\(result.seconds)"

    if minutesString.count == 1 {
        minutesString = "0\(result.minutes)"
    }
    if secondsString.count == 1 {
        secondsString = "0\(result.seconds)"
    }

    var time = "\(hoursString):"
    if result.hours >= 1 {
        time.append("\(minutesString):\(secondsString)")
    }
    else {
        time = "\(minutesString):\(secondsString)"
    }
    return time
}

Then, another function to translate seconds to hour, minute and second. Since the seconds that by layerLayer.player?.currentItem?.duration.seconds will have long Double, so this is needed to become Human Readable.

func timeDivider(seconds: Double) -> (hours: Int, minutes: Int, seconds: Int) {
    guard !(seconds.isNaN || seconds.isInfinite) else {
        return (0,0,0)
    }
    let secs: Int = Int(seconds)
    let hours = secs / 3600
    let minutes = (secs % 3600) / 60
    let seconds = (secs % 3600) % 60
    return (hours, minutes, seconds)
}

Hope it's complete your answer.

Upvotes: 2

Hardik Thakkar
Hardik Thakkar

Reputation: 15991

For Swift to get duration in seconds

if let duration = player.currentItem?.asset.duration {
    let seconds = CMTimeGetSeconds(duration)
    print("Seconds :: \(seconds)")
}

Upvotes: 22

r farnell
r farnell

Reputation: 71

In this example avPlayer is the AVPlayer instance.

I have built a video control that uses the following:

to position the slider use something like this to get playhead's percentage through the movie, you will need to fire this function repeatedly. So i would run the function like:

float scrubberBarLocation = (scrubberBgImageView.frame.size.width / 100.0f) * [self moviePercentage];


- (float)moviePercentage {

    CMTime t1 = [avPlayer currentTime];
    CMTime t2 = avPlayer.currentItem.asset.duration;

    float myCurrentTime = CMTimeGetSeconds(t1);
    float myDuration = CMTimeGetSeconds(t2);

    float percent = (myCurrentTime / myDuration)*100.0f;
    return percent;

}

Then to update the video I would do something like:

- (void)updateVideoPercent:(float)thisPercent {

    CMTime t2 = avPlayer.currentItem.asset.duration;
    float myDuration = CMTimeGetSeconds(t2);

    float result = myDuration * thisPercent /100.0f;

    //NSLog(@"this result = %f",result); // debug

    CMTime seekTime = CMTimeMake(result, 1);

    [avPlayer seekToTime:seekTime];

}

Upvotes: 4

onmyway133
onmyway133

Reputation: 48185

Noted from StitchedStreamPlayer

You should use player.currentItem.duration

- (CMTime)playerItemDuration
{
    AVPlayerItem *thePlayerItem = [player currentItem];
    if (thePlayerItem.status == AVPlayerItemStatusReadyToPlay)
    {        
        /* 
         NOTE:
         Because of the dynamic nature of HTTP Live Streaming Media, the best practice 
         for obtaining the duration of an AVPlayerItem object has changed in iOS 4.3. 
         Prior to iOS 4.3, you would obtain the duration of a player item by fetching 
         the value of the duration property of its associated AVAsset object. However, 
         note that for HTTP Live Streaming Media the duration of a player item during 
         any particular playback session may differ from the duration of its asset. For 
         this reason a new key-value observable duration property has been defined on 
         AVPlayerItem.

         See the AV Foundation Release Notes for iOS 4.3 for more information.
         */     

        return([playerItem duration]);
    }

    return(kCMTimeInvalid);
}

Upvotes: 6

Douglas
Douglas

Reputation: 37781

As of iOS 4.3, you can use the slightly shorter:

self.player.currentItem.duration;

Upvotes: 11

neoneye
neoneye

Reputation: 52231

headers

#import <AVFoundation/AVPlayer.h>
#import <AVFoundation/AVPlayerItem.h>
#import <AVFoundation/AVAsset.h>

code

CMTime duration = self.player.currentItem.asset.duration;
float seconds = CMTimeGetSeconds(duration);
NSLog(@"duration: %.2f", seconds);

frameworks

AVFoundation
CoreMedia

Upvotes: 43

slowman21
slowman21

Reputation: 2515

self.player.currentItem.asset.duration

Got it!

Upvotes: 167

Related Questions