web-dev-qa-db-fra.com

Barre de progression de la timeline pour AVPlayer

AVPlayer est entièrement personnalisable. Malheureusement, il existe des méthodes pratiques dans AVPlayer pour afficher la barre de progression de la ligne de temps.

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

J'ai une barre de progression qui indique comment la vidéo a été lue et combien elle est restée identique à celle de MPMoviePlayer.

Alors, comment obtenir la chronologie de la vidéo de AVPlayer et comment mettre à jour la barre de progression 

Suggère moi. 

18
Sanjeev Rao

Veuillez utiliser le code ci-dessous, extrait du code exemple "AVPlayerDemo" de Apple.

    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);
}

Et dans la méthode syncScrubber, mettez à jour la valeur UISlider ou 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];
    }
} 
24
iOSPawan

Merci à iOSPawan pour le code! J'ai simplifié le code aux lignes nécessaires. Cela pourrait être plus clair pour comprendre le concept. En gros, je l’ai implémenté comme ça et ça marche bien.

Avant de commencer la vidéo:

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

[_player play];

Ensuite, vous devez avoir une méthode pour mettre à jour votre barre de progression:

- (void)updateProgressBar
{
    double duration = CMTimeGetSeconds(_playerItem.duration);
    double time = CMTimeGetSeconds(_player.currentTime);
    _progressView.progress = (CGFloat) (time / duration);
}
18
Raphael
    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))
    }
7
amleszk

Dans mon cas, le code suivant fonctionne avec 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!)   
}
2
Harman

pour la chronologie, je le fais 

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

Et importer le cadre CoreMedia

lengthSlider est UISlider

1
Igor Bidiniuc

J'ai pris les réponses de iOSPawan et Raphael, puis je les ai adaptées à mes besoins. J'ai donc de la musique et UIProgressView qui est toujours en boucle et lorsque vous passez à l'écran suivant, vous revenez à la chanson et à la barre. où ils ont été laissés.

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;
}
0
Tiago Mendes

Réponse rapide pour obtenir des progrès:

private func addPeriodicTimeObserver() {
        // Invoke callback every half second
        let interval = CMTime(seconds: 0.5,
                              preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        // Queue on which to invoke the callback
        let mainQueue = DispatchQueue.main
        // Add time observer
        self.playerController?.player?.addPeriodicTimeObserver(forInterval: interval, queue: mainQueue) { [weak self] time in
            let currentSeconds = CMTimeGetSeconds(time)
            guard let duration = self?.playerController?.player?.currentItem?.duration else { return }
            let totalSeconds = CMTimeGetSeconds(duration)
            let progress: Float = Float(currentSeconds/totalSeconds)
            print(progress)
        }
    }

Réf

0
Zaid Pathan