2012-01-04 9 views
14

AVPlayer полностью настраиваемый, к сожалению, в AVPlayer есть удобные методы для отображения индикатора хода линии времени.Временная шкала Индикатор хода для AVPlayer

AVPlayer *player = [AVPlayer playerWithURL:URL]; 
AVPlayerLayer *playerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];[self.view.layer addSubLayer:playerLayer]; 

У меня есть прогресс бар, показывающий, как игралось видео, и сколько осталось так же, как как MPMoviePlayer.

Так как получить график видео от AVPlayer и как обновить прогресс бар

Предлагайте меня.

+1

Пожалуйста, подумайте об использовании AVPlayerViewController. Это очень просто для воспроизведения (но может не соответствовать вашим потребностям). Просто скажи, если ты не знаешь об этом. (редактировать) - Ой, это три года назад: P. –

ответ

21

Пожалуйста, используйте приведенный ниже код, который из примера яблока кода "AVPlayerDemo".

double interval = .1f; 

    CMTime playerDuration = [self playerItemDuration]; // return player duration. 
    if (CMTIME_IS_INVALID(playerDuration)) 
    { 
     return; 
    } 
    double duration = CMTimeGetSeconds(playerDuration); 
    if (isfinite(duration)) 
    { 
     CGFloat width = CGRectGetWidth([yourSlider bounds]); 
     interval = 0.5f * duration/width; 
    } 

    /* Update the scrubber during normal playback. */ 
    timeObserver = [[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC) 
                  queue:NULL 
                usingBlock: 
                 ^(CMTime time) 
                 { 
                  [self syncScrubber]; 
                 }] retain]; 


- (CMTime)playerItemDuration 
{ 
    AVPlayerItem *thePlayerItem = [player currentItem]; 
    if (thePlayerItem.status == AVPlayerItemStatusReadyToPlay) 
    {   

     return([playerItem duration]); 
    } 

    return(kCMTimeInvalid); 
} 

И в методе syncScrubber обновляется значение UISlider или UIProgressBar.

- (void)syncScrubber 
{ 
    CMTime playerDuration = [self playerItemDuration]; 
    if (CMTIME_IS_INVALID(playerDuration)) 
    { 
     yourSlider.minimumValue = 0.0; 
     return; 
    } 

    double duration = CMTimeGetSeconds(playerDuration); 
    if (isfinite(duration) && (duration > 0)) 
    { 
     float minValue = [ yourSlider minimumValue]; 
     float maxValue = [ yourSlider maximumValue]; 
     double time = CMTimeGetSeconds([player currentTime]); 
     [yourSlider setValue:(maxValue - minValue) * time/duration + minValue]; 
    } 
} 
+0

Что такое переменная timeObserver? –

+0

вам нужно, чтобы timeObserver фактически отменил «наблюдение», и вам также нужно сохранить его, пока вы хотите наблюдать.Вы можете прочитать документ apple о addPeriodicTimeObserverForInterval: queue: usingBlock: –

1

для временной шкалы я сделать это

-(void)changeSliderValue { 

double duration = CMTimeGetSeconds(self.player.currentItem.duration); 

[lengthSlider setMaximumValue:(float)duration]; 

lengthSlider.value = CMTimeGetSeconds([self.player currentTime]); 

int seconds = lengthSlider.value,minutes = seconds/60,hours = minutes/60; 

int secondsRemain = lengthSlider.maximumValue - seconds,minutesRemain = secondsRemain/60,hoursRemain = minutesRemain/60; 

seconds = seconds-minutes*60; 

minutes = minutes-hours*60; 

secondsRemain = secondsRemain - minutesRemain*60; 

minutesRemain = minutesRemain - hoursRemain*60; 

NSString *hourStr,*minuteStr,*secondStr,*hourStrRemain,*minuteStrRemain,*secondStrRemain; 

hourStr = hours > 9 ? [NSString stringWithFormat:@"%d",hours] : [NSString stringWithFormat:@"0%d",hours]; 

minuteStr = minutes > 9 ? [NSString stringWithFormat:@"%d",minutes] : [NSString stringWithFormat:@"0%d",minutes]; 

secondStr = seconds > 9 ? [NSString stringWithFormat:@"%d",seconds] : [NSString stringWithFormat:@"0%d",seconds]; 

hourStrRemain = hoursRemain > 9 ? [NSString stringWithFormat:@"%d",hoursRemain] : [NSString stringWithFormat:@"0%d",hoursRemain]; 

minuteStrRemain = minutesRemain > 9 ? [NSString stringWithFormat:@"%d",minutesRemain] : [NSString stringWithFormat:@"0%d",minutesRemain]; 

secondStrRemain = secondsRemain > 9 ? [NSString stringWithFormat:@"%d",secondsRemain] : [NSString stringWithFormat:@"0%d",secondsRemain]; 

timePlayed.text = [NSString stringWithFormat:@"%@:%@:%@",hourStr,minuteStr,secondStr]; 

timeRemain.text = [NSString stringWithFormat:@"-%@:%@:%@",hourStrRemain,minuteStrRemain,secondStrRemain]; 

И импорт рамка CoreMedia

lengthSlider является UISlider

+0

Я играю песню из url. но в CMTime duration и CMTime currentTime я получаю 0. в чем проблема здесь для значения 0? вы знаете решение? – Moxarth

+0

вот документация об этом: https://developer.apple.com/documentation/avfoundation/avplayeritem/1389386-duration –

+0

этот код продолжительности не помог мне. – Moxarth

16

Благодаря iOSPawan для кода! Я упростил код до нужных строк. Это может быть более понятным для понимания концепции. В основном я реализовал его так, и он отлично работает.

Перед запуском видео:

__weak NSObject *weakSelf = self;  
[_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0/60.0, NSEC_PER_SEC) 
             queue:NULL 
           usingBlock:^(CMTime time){ 
               [weakSelf updateProgressBar]; 
              }]; 

[_player play]; 

Тогда вам нужно иметь способ обновить свой прогресс бар:

- (void)updateProgressBar 
{ 
    double duration = CMTimeGetSeconds(_playerItem.duration); 
    double time = CMTimeGetSeconds(_player.currentTime); 
    _progressView.progress = (CGFloat) (time/duration); 
} 
+0

Вы неправильно используете __block здесь. __block используется, если необходимо использовать назначение переменной в блоке. Вы, вероятно, хотели использовать __weak – kball

+0

Обновили свой код. Но дополнительно - вам нужно сохранить ссылку на объект, возвращаемый addPeriodicTimeObserverForInterval: для продолжения отправки сообщения и последующего его удаления. https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVPlayer_Class/#//apple_ref/occ/instm/AVPlayer/addPeriodicTimeObserverForInterval:queue:usingBlock: – kball

+0

timeObserver следует удалить после остановки воспроизведения '@result \t Объект, соответствующий протоколу NSObject. Вы должны сохранить это возвращенное значение, если вы хотите, чтобы наблюдатель времени вызывал игрока. \t Пропустить этот объект до -removeTimeObserver: отменить наблюдение за временем. ' – Leo

4
let progressView = UIProgressView(progressViewStyle: UIProgressViewStyle.Bar) 
    self.view.addSubview(progressView) 
    progressView.constrainHeight("\(1.0/UIScreen.mainScreen().scale)") 
    progressView.alignLeading("", trailing: "", toView: self.view) 
    progressView.alignBottomEdgeWithView(self.view, predicate: "") 
    player.addPeriodicTimeObserverForInterval(CMTimeMakeWithSeconds(1/30.0, Int32(NSEC_PER_SEC)), queue: nil) { time in 
     let duration = CMTimeGetSeconds(playerItem.duration) 
     progressView.progress = Float((CMTimeGetSeconds(time)/duration)) 
    } 
1

В моем случае, следующий код работает Swift 3:

var timeObserver: Any? 
override func viewDidLoad() { 
    ........ 
    let interval = CMTime(seconds: 0.05, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) 
    timeObserver = avPlayer.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { elapsedTime in 
      self.updateSlider(elapsedTime: elapsedTime)  
     }) 
} 

func updateSlider(elapsedTime: CMTime) { 
    let playerDuration = playerItemDuration() 
    if CMTIME_IS_INVALID(playerDuration) { 
     seekSlider.minimumValue = 0.0 
     return 
    } 
    let duration = Float(CMTimeGetSeconds(playerDuration)) 
    if duration.isFinite && duration > 0 { 
     seekSlider.minimumValue = 0.0 
     seekSlider.maximumValue = duration 
     let time = Float(CMTimeGetSeconds(elapsedTime)) 
     seekSlider.setValue(time, animated: true) 
    } 
} 

private func playerItemDuration() -> CMTime { 
    let thePlayerItem = avPlayer.currentItem 
    if thePlayerItem?.status == .readyToPlay { 
     return thePlayerItem!.duration 
    } 
    return kCMTimeInvalid 
} 

override func viewDidDisappear(_ animated: Bool) { 
    super.viewDidDisappear(animated) 
    avPlayer.removeTimeObserver(timeObserver!) 
} 
0

Я принял ответы f iOSPawan и Raphael, а затем адаптированы к моим потребностям. Итак, у меня есть музыка и UIProgressView, который всегда находится в цикле, и когда вы переходите к следующему экрану и возвращаетесь к песне, а панель продолжает оставаться там, где они остались.

Код:

@interface YourClassViewController(){ 

    NSObject * periodicPlayerTimeObserverHandle; 
} 
@property (nonatomic, strong) AVPlayer *player; 
@property (nonatomic, strong) UIProgressView *progressView; 

-(void)viewWillAppear:(BOOL)animated{ 
    [super viewWillAppear:animated]; 

    if(_player != nil && ![self isPlaying]) 
    { 
     [self musicPlay]; 
    } 
} 


-(void)viewWillDisappear:(BOOL)animated 
{ 
    [super viewWillDisappear:animated]; 

    if (_player != nil) { 

     [self stopPlaying]; 
    } 
} 

// ---------- 
//  PLAYER 
// ---------- 

-(BOOL) isPlaying 
{ 
    return ([_player rate] > 0); 
} 

-(void) musicPlay 
{ 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(playerItemDidReachEnd:) 
               name:AVPlayerItemDidPlayToEndTimeNotification 
               object:[_player currentItem]]; 

    __weak typeof(self) weakSelf = self; 
    periodicPlayerTimeObserverHandle = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0/60.0, NSEC_PER_SEC) 
                      queue:NULL 
                     usingBlock:^(CMTime time){ 
                      [weakSelf updateProgressBar]; 
                     }]; 
    [_player play]; 
} 


-(void) stopPlaying 
{ 
    @try { 

     if(periodicPlayerTimeObserverHandle != nil) 
     { 
      [_player removeTimeObserver:periodicPlayerTimeObserverHandle]; 
      periodicPlayerTimeObserverHandle = nil; 
     } 

     [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 
     [_player pause]; 
    } 
    @catch (NSException * __unused exception) {} 
} 


-(void) playPreviewSong:(NSURL *) previewSongURL 
{ 
    [self configureAVPlayerAndPlay:previewSongURL]; 
} 


-(void) configureAVPlayerAndPlay: (NSURL*) url { 

    if(_player) 
     [self stopPlaying]; 

    AVAsset *audioFileAsset = [AVURLAsset URLAssetWithURL:url options:nil]; 
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:audioFileAsset]; 
    _player = [AVPlayer playerWithPlayerItem:playerItem]; 
    [_player addObserver:self forKeyPath:@"status" options:0 context:nil]; 

    CRLPerformBlockOnMainThreadAfterDelay(^{ 
     NSError *loadErr; 
     if([audioFileAsset statusOfValueForKey:@"playable" error:&loadErr] == AVKeyValueStatusLoading) 
     { 
      [audioFileAsset cancelLoading]; 
      [self stopPlaying]; 
      [self showNetworkError:NSLocalizedString(@"Could not play file",nil)]; 
     } 
    }, NETWORK_REQUEST_TIMEOUT); 
} 


- (void)updateProgressBar 
{ 

    double currentTime = CMTimeGetSeconds(_player.currentTime); 
    if(currentTime <= 0.05){ 
     [_progressView setProgress:(float)(0.0) animated:NO]; 
     return; 
    } 

    if (isfinite(currentTime) && (currentTime > 0)) 
    { 
     float maxValue = CMTimeGetSeconds(_player.currentItem.asset.duration); 
     [_progressView setProgress:(float)(currentTime/maxValue) animated:YES]; 
    } 
} 


-(void) showNetworkError:(NSString*)errorMessage 
{ 
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"No connection", nil) message:errorMessage preferredStyle:UIAlertControllerStyleAlert]; 
    [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { 
     // do nothing 
    }]]; 

    [self presentViewController:alert animated:YES completion:nil]; 
} 


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 

    if (object == _player && [keyPath isEqualToString:@"status"]) { 
     if (_player.status == AVPlayerStatusFailed) { 
      [self showNetworkError:NSLocalizedString(@"Could not play file", nil)]; 
     } else if (_player.status == AVPlayerStatusReadyToPlay) { 
      NSLog(@"AVPlayerStatusReadyToPlay"); 
      [TLAppAudioAccess setAudioAccess:TLAppAudioAccessType_Playback]; 
      [self musicPlay]; 

     } else if (_player.status == AVPlayerItemStatusUnknown) { 
      NSLog(@"AVPlayerItemStatusUnknown"); 
     } 
    } 
} 


- (void)playerItemDidReachEnd:(NSNotification *)notification { 

    if ([notification.object isEqual:self.player.currentItem]) 
    { 
     [self.player seekToTime:kCMTimeZero]; 
     [self.player play]; 
    } 
} 


-(void) dealloc{ 

    @try { 
     [_player removeObserver:self forKeyPath:@"status"]; 
    } 
    @catch (NSException * __unused exception) {} 
    [self stopPlaying]; 
    _player = nil; 
} 
Смежные вопросы