Avplayer Stops Playing and Doesn't Resume Again

AVPlayer won't resume playing after being paused

try adding observer in only viewDidLoad() or viewWillAppear() adding observer should only done in one time for these kind of problems.
Still you have any problem feel free to ask me.

AVPlayer stops playing and doesn't resume again

Yes, it stops because the buffer is empty so it has to wait to load more video. After that you have to manually ask for start again. To solve the problem I followed these steps:

1) Detection: To detect when the player has stopped I use the KVO with the rate property of the value:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"rate"] )
{

if (self.player.rate == 0 && CMTimeGetSeconds(self.playerItem.duration) != CMTimeGetSeconds(self.playerItem.currentTime) && self.videoPlaying)
{
[self continuePlaying];
}
}
}

This condition: CMTimeGetSeconds(self.playerItem.duration) != CMTimeGetSeconds(self.playerItem.currentTime) is to detect the difference between arriving at the end of the video or stopping in the middle

2) Wait for the video to load - If you continue playing directly you will not have enough buffer to continue playing without interruption. To know when to start you have to observe the value playbackLikelytoKeepUp from the playerItem (here I use a library to observe with blocks but I think it makes the point):

-(void)continuePlaying
{

if (!self.playerItem.playbackLikelyToKeepUp)
{
self.loadingView.hidden = NO;
__weak typeof(self) wSelf = self;
self.playbackLikelyToKeepUpKVOToken = [self.playerItem addObserverForKeyPath:@keypath(_playerItem.playbackLikelyToKeepUp) block:^(id obj, NSDictionary *change) {
__strong typeof(self) sSelf = wSelf;
if(sSelf)
{
if (sSelf.playerItem.playbackLikelyToKeepUp)
{
[sSelf.playerItem removeObserverForKeyPath:@keypath(_playerItem.playbackLikelyToKeepUp) token:self.playbackLikelyToKeepUpKVOToken];
sSelf.playbackLikelyToKeepUpKVOToken = nil;
[sSelf continuePlaying];
}
}
}];
}

And that's it! problem solved

Edit: By the way the library used is libextobjc

AVPlayer stops playing video after buffering

Since iOS 10.x, you can make some buffer settings, for example you can decide how many seconds you'll need to buffering your video:

    if #available(iOS 10.0, tvOS 10.0, OSX 10.12, *) {
avPlayer?.automaticallyWaitsToMinimizeStalling = .playWhenBufferNotEmpty
//preferredForwardBufferDuration -> default is 0, which means `AVPlayer` handle it independently, try more seconds like 5 or 10.
playerItem.preferredForwardBufferDuration = TimeInterval(5)
}

Swift iOS -AVPlayer Video Freezes / Pauses When App Comes Back from Background

According to the Apple Docs when a video is playing and the app is sent to the background the player is automatically paused:

Sample Image

What they say to do is remove the AVPlayerLayer (set to nil) when the app is going to the background and then reinitialize it when it comes to the foreground:

Sample Image

And the best way they say to handle this is in the applicationDidEnterBackground and the applicationDidBecomeActive:

Sample Image

I used NSNotification to listen for the background and foreground events and set functions to pause the player & set the playerLayer to nil (both for background event) and then reinitialized the playerLayer & played the player for the foreground event. These are the Notifications I used .UIApplicationWillEnterForeground and .UIApplicationDidEnterBackground

What I've come to find out is that for some reason if you long press the Home button and that screen that pops up that says "What can I help you with" appears, if you press the Home button again to go back to your app the video will be frozen and using the 2 Notifications from above won't prevent it. The only way I found to prevent this is to also use the Notifications .UIApplicationWillResignActive and .UIApplicationDidBecomeActive. If you don't add these in addition to the above Notifications then your video will be frozen on the Home button long press and back. The best way that I've found to prevent all frozen scenarios is to use all 4 Notifications.

2 things I had to do differently from my code above was to set player and playerLayer class variables as optionals instead of implicitly unwrapped optionals and I also added an extension to the AVPlayer class to check to see if it's playing or not in iOS 9 or below. In iOS 10 and above there is a built in method .timeControlStatus AVPlayer timer status

my code above:

var player: AVPlayer?
var playerLayer: AVPlayerLayer?

Add an extension to the AVPlayer to check the state of the AVPlayer in iOS 9 or below:

import AVFoundation

extension AVPlayer{

var isPlaying: Bool{
return rate != 0 && error == nil
}
}

Here is the completed code below:

var player: AVPlayer?
var playerLayer: AVPlayerLayer? //must be optional because it will get set to nil on background event

override func viewDidLoad() {
super.viewDidLoad()

// background event
NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: UIApplication.didEnterBackgroundNotification, object: nil)

// foreground event
NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: UIApplication.willEnterForegroundNotification, object: nil)

// add these 2 notifications to prevent freeze on long Home button press and back
NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: UIApplication.willResignActiveNotification, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: UIApplication.didBecomeActiveNotification, object: nil)

configurePlayer()
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

// this is also for the long Home button press
if let player = player{
if #available(iOS 10.0, *) {
if player.timeControlStatus == .paused{
player.play()
}
} else {
if player.isPlaying == false{
player.play()
}
}
}
}

@objc fileprivate func configurePlayer(){

let url = Bundle.main.url(forResource: "myVideo", withExtension: ".mov")

player = AVPlayer.init(url: url!)
playerLayer = AVPlayerLayer(player: player!)
playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer?.frame = view.layer.frame

player?.actionAtItemEnd = AVPlayerActionAtItemEnd.none

player?.play()

view.layer.insertSublayer(playerLayer!, at: 0)

NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
}

@objc fileprivate func playerItemReachedEnd(){
// this works like a rewind button. It starts the player over from the beginning
player?.seek(to: kCMTimeZero)
}

// background event
@objc fileprivate func setPlayerLayerToNil(){
// first pause the player before setting the playerLayer to nil. The pause works similar to a stop button
player?.pause()
playerLayer = nil
}

// foreground event
@objc fileprivate func reinitializePlayerLayer(){

if let player = player{

playerLayer = AVPlayerLayer(player: player)

if #available(iOS 10.0, *) {
if player.timeControlStatus == .paused{
player.play()
}
} else {
// if app is running on iOS 9 or lower
if player.isPlaying == false{
player.play()
}
}
}
}

DON'T FORGET TO ADD THE isPlaying EXTENSION TO THE AVPlayer

How To Resume my Video which I did pause in AVplayer?

This is because you are initialising Player again.
Do this trick

Declare one variable in .h like
BOOL isResumed = false;

Now update your code to this

-(IBAction)playVideo:(id)sender {
if(isResumed) {
[self.player play];
self.pauseButton.enabled = YES;
}
else{
NSURL *videoURL = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"sample_Video" ofType:@"mp4"]];
self.player = [AVPlayer playerWithURL:videoURL];

AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
playerLayer.frame = self.videoView.bounds;
[self.videoView.layer addSublayer:playerLayer];
[self.player play];
self.slider.hidden = NO;
self.playButton.enabled = NO;
self.pauseButton.enabled = YES;
}
}

-(IBAction)pauseVideo:(id)sender {
isResumed = true;
[self.player pause];
self.playButton.enabled = YES;
self.pauseButton.enabled = NO;

}

AVPlayer doesn't resume playback on endinterruption on iPhone but does so on iPad

It turns out that this is a known bug in iOS, which requires some careful work-arounds in the applicationDidBecomeActive to handle beginInterruption. Sadly, I couldn't figure out another solution.



Related Topics



Leave a reply



Submit