MPMoviePlayerController stalling frequently - ios

I'm using MPMoviePlayerController to play remote mp4 files which are not quite big, about 20 - 30 MB each. This is how the player is set up:
player.movieSourceType = MPMovieSourceTypeFile;
player.shouldAutoplay = YES;
The problem is that the player stops frequently with the following console output:
Took background task assertion ... for playback stall // stops
Ending background task assertion ... for playback stall // resumes
I possibly could have taken this behavior as granted, but on the other hand when I try to play the video in other streaming player (e.g. GoodPlayer) - it plays nicely and never stops. So the problem is in my approach itself, not in the Internet connection.
Are there any ways to make MPMoviePlayerController to load seamlessly, or do I have to change components used in my app? If yes, which ones would you recommend?

Try to assign (declare) MPMoviePlayerController as #property in your .h file, rather than a local variable.

Related

AVPlayers buffer slower with time

I am working on an app that supports video playback similar to what snapchat is doing (you can tap through a bunch of remote videos). After tapping through a handful of videos, buffering seems to become slower, and comes to a complete stop at some point of you keep going. Sounds a lot like a memory leak to me.
After diving into it in Instruments, I found that all AVPlayers, AVPlayerItems, and AVPlayerLayers get deallocated correctly. The only leaks I find are these:
However, by studying the Connections graph and the Memory Usage, I can clearly see that even after dismissing the video player and sitting in the camera view of the app, some buffering is still occurring. The network activity stays fairly high and the memory slowly grows.
To get a better understanding of how I do video playback, here's a short explanation. I have a VideoPlayer object that I only initiate one of, then I give it new assets to play. Before giving it a new asset, I call stopBuffering, just to make sure nothing gets left behind (we all see how successful that was).
- (void)stopBuffering
{
[self removeObservers];
[self.player pause];
[self.avPlayer replaceCurrentItemWithPlayerItem:nil];
self.avPlayer = nil;
self.player = nil;
self.playerItem = nil;
}
I have verified that this method gets called every time a new asset is loaded.
This is the All Heap & Anonymous VM graph, in which the first generation is before any video playback, and the third generation is after leaving the video player. In between, I quickly jumped through about 20 videos.
The reason for this was that I began loading values with loadValuesAsynchronouslyForKeys whenever I loaded an asset, but if I moved on the next asset before this method completed, I never canceled it. After storing the AVURLAsset as a property and then calling
[self.asset cancelLoading];
at appropriate times, the problem disappeared.

How to stop downloading a streaming HLS video after navigating away from AVPlayer view?

I am trying to play a HLS stream using AVPlayer. The player plays the stream fine, however, after navigating away from the player view, it does not seem to stop downloading data for the HLS stream. I see a network data spinner on the status bar for a few minutes after navigating away from the AVPlayer view.
I have tried cancelPendingSeeks, cancelLoading, and tried removing the AVPlayerLayer from its superlayer using removeFromSuperLayer, however, none of these seems to solve the issue — there is still a spinner on the status bar.
This spinner is seen on actual device, but there is no spinner on the simulator. I am certain the video is being downloaded; I can see the data usage in network monitoring apps. How can I fix this?
Call pause on your AVPlayer object, then replace the item with a nil item, and set your video player to nil.
[anAVPlayerObject pause];
[anAVPlayerObject replaceCurrentItemWithPlayerItem:nil];
anAVPlayerObject = nil;

Multiple AVPlayers ducking?

I have a relatively simple setup involving 1 AVPlayer looping some ambient audio in the background and a second player playing a shorter sound at certain points.
What I've observed is that when the short sound is played, I hear the ambient clip cut out for about a second while there is a staticy pop. It then proceeds to continue playing while the short sound is played at the same time. This only happens on device - it's not noticeable on the sim, which seems to point to a potential performance issue.
I can't quite figure out why the first AVPlayer has this blip. Here is the code for the ambient player:
NSString *path = [[NSBundle mainBundle] pathForResource:kAmbientTrack ofType:#"mp3"];
_ambientPlayer = [AVPlayer playerWithURL:[NSURL fileURLWithPath:path]];
[self.ambientPlayer play];
It's slightly more complex, as I also listen for a notification when it ends and the restart it, but this issue happens even during the initial play before any looping occurs.
So while that is going in the background, I play another clip like so:
self.announcementPlayer = [[AVPlayer alloc] initWithURL:[myObject audioPathUrl]];
[self.announcementPlayer play];
So nothing too unique there - just two AVPlayers playing.
The only other piece of interest is how I set up the audio session when the app launches.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
I'm doing this to allow the user to play music from other apps in the background.
I think my code is pretty straight forward, and really have no idea why I would be getting this blip.
I fixed this somewhat by changing the audio players to be AVAudioPlayers. That resolved the popping in most cases. However, I still get the ducking. Specifically, if I play an AVAudioPlayer and, while it's still playing, I start to load an AVPlayer, it cuts off the AVAudioPlayer for about a second. Here's what I'm talking about:
[myAVAudioPlayer play];
AVPlayer *player = [AVPlayer playerWithURL:[self.currentExercise videoPathUrl]];
The addition of that second line causes the first second or so of myAVAudioPlayer to be silent.

How can I avoid the AVPlayerLayer `setPlayer` audio blip?

I have an application that plays video using AVPlayer and AVPlayerLayer. In my app, I'm able to play audio when the app is locked by setting the player property of my AVPlayerLayer to nil when the application enters the background:
-(void)appEnteredBackgroundNotification:(NSNotification *)notification {
[[self playerLayer] setPlayer:nil];
}
However, when I do this, the audio will lag/blip for around 0.5 seconds. This sounds really really bad for the end user. Same goes for when the app enters foreground and I re-set the player property.
How can I avoid this audio blip? As a test I've tried removing the player in a background thread to no avail.
Update: I spoke with an Apple engineer at WWDC and they said that this issue is a bug on their end (so far not fixed in iOS 9) and this approach is the correct approach. Great...
I think may not you call pause before setting to nil and vice versa. And, try calling prepare before play.

AVAudioPlayer initializes fine, but refuses to play

I have a curious problem with the AVAudioPlayer class. I create a player, get a non-nil reference from the initializer, but the -play message returns NO and the player doesn’t play. I have implemented the player delegate methods and -audioPlayerDecodeErrorDidOccur:error: is not called. The error from the initializer is not set.
I have checked the usual suspects. I do set the audio session category, I also activate it. The audio file for the player is recorded by the app just before playing. When I initialize the player with a different sound (from the app bundle), it plays. When I copy the recorded file into the app bundle and then initialize the player with this copy, it plays. When I check for file existence before loading it, the file is there, is readable and has a non-zero size.
I have tried both the Simulator and the device, no difference.
What am I missing?
It helped to wait 1/10 of a second between the end of recording and playback:
dispatch_time_t moment = dispatch_time(DISPATCH_TIME_NOW, 1e8);
dispatch_after(moment, dispatch_get_main_queue(), ^{
[statusLabel setText:#"Playing"];
[self setPlayer:[[AVAudioPlayer alloc]
initWithContentsOfURL:[self recordingURL]
error:NULL]];
[player play];
});
This of course is not a proper solution, so after a bit of searching I realized I wasn’t properly finishing the recording, leaving the file buffers unflushed before creating the player. Now that I flush the buffers before calling the player everything works fine.

Resources