2012-01-04 30 views
14

AVPlayer có thể tùy chỉnh hoàn toàn, rất tiếc, có các phương pháp tiện lợi trong AVPlayer để hiển thị thanh tiến trình dòng thời gian.Thanh tiến độ dòng thời gian cho AVPlayer

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

Tôi có thanh tiến trình cho biết cách phát video và số tiền còn lại giống như MPMoviePlayer.

Vậy làm thế nào để có được thời gian của video từ AVPlayer và làm thế nào để cập nhật các thanh tiến trình

Đề nghị cho tôi.

+1

Hãy xem xét sử dụng AVPlayerViewController. Nó rất đơn giản để phát lại (nhưng có thể không phù hợp với nhu cầu của bạn). Chỉ cần nói trong trường hợp bạn không nhận thức được nó. (sửa) - Rất tiếc đây là ba năm trước: P. –

Trả lời

21

Vui lòng sử dụng mã dưới đây là từ ví dụ táo mã "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); 
} 

Và đồng bộPhương pháp sàn nâng cấp giá trị UISlider hoặc 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 biến là gì? –

+0

bạn cần timeObserver để thực sự hủy 'quan sát' và bạn cũng cần giữ lại nó, miễn là bạn muốn quan sát ..Bạn có thể đọc docu của apple về addPeriodicTimeObserverForInterval: queue: usingBlock: –

1

cho timeline tôi làm

-(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]; 

Và khuôn khổ CoreMedia nhập khẩu

lengthSlider đây là UISlider

+0

Tôi đang phát bài hát từ url. nhưng tại thời gian CMTime và thời gian hiện tại CMTime, tôi nhận được 0. vấn đề ở đây cho 0 giá trị là gì? bạn có biết giải pháp không? – Moxarth

+0

đây là tài liệu về điều này: https://developer.apple.com/documentation/avfoundation/avplayeritem/1389386-duration –

+0

mã thời lượng đó cũng không giúp ích cho tôi. – Moxarth

16

Nhờ iOSPawan cho mã! Tôi đã đơn giản hóa mã cho các dòng cần thiết. Điều này có thể rõ ràng hơn để hiểu khái niệm. Về cơ bản tôi đã thực hiện nó như thế này và nó hoạt động tốt.

Trước khi bắt đầu video:

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

[_player play]; 

Sau đó, bạn cần phải có một phương pháp để cập nhật thanh tiến trình của bạn:

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

Bạn đang sử dụng sai __block ở đây. __block được sử dụng nếu cần sử dụng gán cho biến trong khối. Bạn có thể muốn sử dụng __weak – kball

+0

Đã cập nhật mã của bạn. Nhưng ngoài ra - bạn cần phải giữ một tham chiếu đến đối tượng được trả về bởi addPeriodicTimeObserverForInterval: để nó tiếp tục gửi tin nhắn và sau đó bạn có thể xóa nó sau này. https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVPlayer_Class/#//apple_ref/occ/instm/AVPlayer/addPeriodicTimeObserverForInterval:queue:usingBlock: – kball

+0

timeObserver nên được xóa sau khi ngừng phát '@result \t Một đối tượng phù hợp với giao thức NSObject. Bạn phải giữ lại giá trị trả về này miễn là bạn muốn người quan sát thời gian được người chơi gọi. \t Vượt qua đối tượng này để -removeTimeObserver: để hủy bỏ thời gian quan sát.' – 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

Trong trường hợp của tôi, đoạn mã sau trình 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

Tôi lấy câu trả lời f rom iOSPawan và Raphael và sau đó thích nghi với nhu cầu của tôi. Vì vậy, tôi có âm nhạc và UIProgressView luôn luôn trong vòng lặp và khi bạn đi đến màn hình tiếp theo và trở lại bài hát và thanh tiếp tục nơi họ còn lại.

Code:

@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; 
} 
Các vấn đề liên quan