Reputation: 2515
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
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
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
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
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
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
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
Reputation: 37781
As of iOS 4.3, you can use the slightly shorter:
self.player.currentItem.duration;
Upvotes: 11
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