Как получить Duration от AVPlayer (не AVAudioPlayer)?
Я хотел бы сделать UISlider(скруббер) для моего AVPlayer. Но поскольку это не AVAudioPlayer, он не имеет встроенной длительности. Любое предложение о том, как создать слайдер для быстрой перемотки вперед, назад и прогресса воспроизведения?
Я прочитал документ на AVPlayer, он имеет встроенный seekToTime или seekToTime: толерантность Before: толерантность After:. Я не очень понимаю это. Будет ли это ответом для моего слайдера? AVPlayer также имеет addPeriodicTimeObserverForInterval:queue:usingBlock: это для получения продолжительности моего трека? Может кто-нибудь дать мне пример того, как реализовать этот код? Я не фанат документации Apple. Кажется, это очень трудно понять.
9 ответов
заголовки
#import <AVFoundation/AVPlayer.h>
#import <AVFoundation/AVPlayerItem.h>
#import <AVFoundation/AVAsset.h>
код
CMTime duration = self.player.currentItem.asset.duration;
float seconds = CMTimeGetSeconds(duration);
NSLog(@"duration: %.2f", seconds);
рамки
AVFoundation
CoreMedia
Для Свифта получить длительность в секундах
if let duration = player.currentItem?.asset.duration {
let seconds = CMTimeGetSeconds(duration)
print(seconds)
}
Начиная с iOS 4.3, вы можете использовать немного короче:
self.player.currentItem.duration;
Отмечено от StitchedStreamPlayer
Вы должны использовать 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);
}
В этом примере avPlayer является экземпляром AVPlayer.
Я построил элемент управления видео, который использует следующее:
чтобы позиционировать слайдер, используйте что-то вроде этого, чтобы получить процент воспроизведения в фильме, вам нужно будет запускать эту функцию несколько раз. Так что я бы запустить функцию как:
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;
}
Затем, чтобы обновить видео, я бы сделал что-то вроде:
- (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];
}
Хотя ответы здесь правильные, я считаю полезным добавить, что вы можете спросить
AVAsset
чтобы загрузить продолжительность (или любое другое свойство) и получить обратный вызов, как только это произойдет, чтобы вы могли получить доступ к значению. Это ответ на многие комментарии о том, что значение
player.currentItem.asset.duration
возвращает 0,00, пока активы не будут загружены.
Он использует AVAsynchronousKeyValueLoading следующим образом:
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)
}
}
Этот фрагмент взят из примера кода « Воспроизведение пользовательского аудио с помощью собственного проигрывателя » от Apple.
Swift 5
Поместите этот код в какую-нибудь функцию, которая вам нужна:
let duration = player.currentItem?.duration.seconds ?? 0
let playDuration = formatTime(seconds: duration) //Duration RESULT
Создайте функцию с именем: 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
}
Затем еще одна функция для перевода секунд в часы, минуты и секунды. Поскольку секунды, которые выполняет layerLayer.player?.CurrentItem?.Duration.seconds, будут иметь long Double, это необходимо, чтобы сделать его удобочитаемым.
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)
}
Надеюсь, это исчерпывающий ответ.
Проверьте это, используя ниже, вы также получаете прогресс
Быстрый
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)
}
Цель С
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];
});