AVPlayer stops playing and doesn't resume again

前端 未结 9 1558
离开以前
离开以前 2020-12-02 06:03

In my application I have to play audio files stored on a web server. I\'m using AVPlayer for it. I have all the play/pause controls and all delegates and observ

9条回答
  •  感情败类
    2020-12-02 06:56

    I am working with video files, so there's more to my code than you need, but the following solution should pause the player when it hangs, then check every 0.5 second to see whether we've buffered enough to keep up. If so, it restarts the player. If the player hangs for more than 10 seconds without restarting, we stop the player and apologize to the user. This means you need the right observers in place. The code below is working pretty well for me.

    properties defined / init'd in a .h file or elsewhere:

    AVPlayer *player;  
    int playerTryCount = -1; // this should get set to 0 when the AVPlayer starts playing
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    

    partial .m:

    - (AVPlayer *)initializePlayerFromURL:(NSURL *)movieURL {
      // create AVPlayer
      AVPlayerItem *videoItem = [AVPlayerItem playerItemWithURL:movieURL];
      AVPlayer *videoPlayer = [AVPlayer playerWithPlayerItem:videoItem];
    
      // add Observers
      [videoItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
      [self startNotificationObservers]; // see method below
      // I observe a bunch of other stuff, but this is all you need for this to work
    
      return videoPlayer;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
      // check that all conditions for a stuck player have been met
      if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
          if (self.player.currentItem.playbackLikelyToKeepUp == NO &&
              CMTIME_COMPARE_INLINE(self.player.currentTime, >, kCMTimeZero) && 
              CMTIME_COMPARE_INLINE(self.player.currentTime, !=, self.player.currentItem.duration)) {
    
                  // if so, post the playerHanging notification
                  [self.notificationCenter postNotificationName:PlayerHangingNotification object:self.videoPlayer];
          }
      }
    }
    
    - (void)startNotificationObservers {
        [self.notificationCenter addObserver:self 
                                    selector:@selector(playerContinue)
                                       name:PlayerContinueNotification
                                     object:nil];    
    
        [self.notificationCenter addObserver:self 
                                    selector:@selector(playerHanging)
                                       name:PlayerHangingNotification
                                     object:nil];    
    }
    
    // playerHanging simply decides whether to wait 0.5 seconds or not
    // if so, it pauses the player and sends a playerContinue notification
    // if not, it puts us out of our misery
    - (void)playerHanging {
        if (playerTryCount <= 10) {
    
          playerTryCount += 1;
          [self.player pause];
          // start an activity indicator / busy view
          [self.notificationCenter postNotificationName:PlayerContinueNotification object:self.player];
    
        } else { // this code shouldn't actually execute, but I include it as dummyproofing
    
          [self stopPlaying]; // a method where I clean up the AVPlayer,
                              // which is already paused
    
          // Here's where I'd put up an alertController or alertView
          // to say we're sorry but we just can't go on like this anymore
        }
    }
    
    // playerContinue does the actual waiting and restarting
    - (void)playerContinue {
        if (CMTIME_COMPARE_INLINE(self.player.currentTime, ==, self.player.currentItem.duration)) { // we've reached the end
    
          [self stopPlaying];
    
        } else if (playerTryCount  > 10) // stop trying
    
          [self stopPlaying];
          // put up "sorry" alert
    
        } else if (playerTryCount == 0) {
    
          return; // protects against a race condition
    
        } else if (self.player.currentItem.playbackLikelyToKeepUp == YES) {
    
          // Here I stop/remove the activity indicator I put up in playerHanging
          playerTryCount = 0;
          [self.player play]; // continue from where we left off
    
        } else { // still hanging, not at end
    
            // create a 0.5-second delay to see if buffering catches up
            // then post another playerContinue notification to call this method again
            // in a manner that attempts to avoid any recursion or threading nightmares 
            playerTryCount += 1;
            double delayInSeconds = 0.5;
            dispatch_time_t executeTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
            dispatch_after(executeTime, dispatch_get_main_queue(), ^{
    
              // test playerTryCount again to protect against changes that might have happened during the 0.5 second delay
              if (playerTryCount > 0) {
                  if (playerTryCount <= 10) {
                    [self.notificationCenter postNotificationName:PlayerContinueNotification object:self.videoPlayer];
                  } else {
                    [self stopPlaying];
                    // put up "sorry" alert
                  }
              }
            });
    }
    

    Hope it helps!

提交回复
热议问题