AVPlayers buffer slower with time - ios

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.

Related

truncated sound clips on IOS

The essence of the problem is that on IOS only, playing
sound clips using
MediaManager.createMedia(clip, "audio/wav", null).play();
sometimes the playback is truncated. This is especially obvious
when the clip is several seconds long, but the point at which the
clip is truncated varies; occasionally the whole clip plays.
I've so far been unsuccessful creating a simple test case - in isolation
from my apps, the clip plays in full, so it seems like something in normal
background activity in the full app is interfering with audio playback.
I'm pretty sure I've found the root cause. MediaManager.createMedia creates an instance of Media, and when that object is garbage collected, it's finalize method is called, and a side effect of that causes the audio to be stopped.
A quick and dirty way to prevent this is to save a pointer to the media object.
This ought to be accomplished by internal bookkeeping in playaudio.

Transitioning to background, AVPlayer Video has small audio gap

I have followed the many helpful previous questions to get my AVPlayer successfully streaming video when my app goes to the background. There are two methods described on Apple's QA1668 and they both work for my stream urls.
The problem is that there is a noticeable audio gap during the transition that is identical for both methods. On my iPhone 6 in release mode I would say the gap is less than 0.5 seconds, which may not seem terrible but if I'm playing something like a music video this is very distracting.
After more testing it looks like this gap actually occurs when I remove the AVPlayerLayer (or, if I am using the other method, when I disable the AVMediaCharacteristicVisual tracks) as I have determined it will still happen if I hook those actions up to a button rather than the backgrounding state.
My guess is that is has something to do with the audio re-syncing to the new video state of the AVPlayer but really I have no clue. Any help would be greatly appreciated!

AVPlayer "Cannot Complete Action"

Im getting this error after playing several videos:
Error Domain=AVFoundationErrorDomain Code=-11819 "Cannot Complete Action" UserInfo=0x1d83a7f0 {NSLocalizedRecoverySuggestion=Try again later., NSLocalizedDescription=Cannot Complete Action}
My play function looks like this:
- (void)playItem:(AVPlayerItem*)item
playerView:(PlayerView*)playerView
doReset:(BOOL)reset
{
// if it's different item or we want to reset then replace item and rewind player to zero
if([player currentItem] != item || reset)
{
[player pause];
[player replaceCurrentItemWithPlayerItem:item];
[player seekToTime:kCMTimeZero];
}
// set the view's player
[playerView setPlayer:player];
[player play];
The player only stops working after a couple of hundred attempts.
I have 5 PlayerView's which are 5 Videos that when touched will play. To get to a point where i get this error (only by looking at AVPlayer instance error property) i have to touch these buttons severl times (like mentioned before it goes from couple of hundred to thousand times).
I have tried all kinds of stuff to fix this but no success. I know that if i add this line [playerView setVideoFillMode:AVLayerVideoGravityResizeAspectFill] will make this error come early ( bebore 200 times).
What i haven't tried is to use a single PlayerView that will be reused for each button, but i will have to play more than one video at the same time in the future and i think it will bring out this problem after a while.
Googling this error or wither searching StackOverflow didn't bring me no solutions so far.
Does anyone experience this kin of problems? It happens either in iOS5 or iOS6 (not targeting bellow 5) with ARC.
Thanks in advance
I'm not set-up to try your code, but have you tried wrapping replaceCurrentItemWithPlayerItem: in #autoreleasepool to force ARC to do some cleanup?
Alternately, have you checked allocations via Instruments?
From your description, it sounds a lot like a leak. You may have found a bug in AVPlayer (I doubt it's gotten the kind of exercise you're giving it!) and, if so, you should definitely file a bug with Apple, but there may be some things you can do to work around.
Is there a reason you want to keep re-using the same player? Have you tried ditching the old player and creating a new one, rather than replaceCurrentItemWithPlayerItem: ?
Let us know what Instruments-Allocations shows. I bet that's key in getting to the bottom of this.

Controlling the pre-buffering of items in the avqueueplayer

Please can someone tell me if there is a way to selectively pre-buffer the avplayeritems in the AVQueuePlayer array rather than leaving it down to the AVQueuePlayer automatic way of only loading the next item in as the first item finishes playing. 
I'm loading a sequence of 4 short movie clips and I'd like to pre-cache them before telling the AVQueuePlayer to play the array. Is there actually a way of getting under the bonet of avqueueplayer and controlling the pre-buffering as desired?
Right now with its default lazy-loading behaviour, I'm getting some chugging in the playback, with the clips not even playing-out properly because the AVQueuePlayer is trying to loading-in the next clip while it's playing. I'm doing this on iPad deployed to the actual device and not with the simulator.
You can do this with the mpmovieplayer by calling [player prepareToPlay]; which basically manually initiates the loading of each video file you want and then you can check for the completeion of loading by watching for the mpmovieplayerLoadstateDidChange notification and testing the loadState value to see if it has fully loaded ,then telling the mpmovieplayer to play. How can you effectively do a similar thing with AVQueuePlayer?
Is this even possible or have I discovered one of the major drawbacks of the AVQueuePlayer?
Nice suggestion with the playerObserver Stephen, but what is needed is something like you need to be able to explicitly get individual items to load into memory and then tell the AVQueuePlayer 'do not play the first item in the array until ALL items in the array are loaded into memory' There currently seems to be no way to start even the second item in the array loading until the first one is coming to an end!
As a slightly separate issue, I've also noticed some weirdness in the AVQueuePlayer where, if you load two of the same source video file into the array (both referenced as two completely separate AVPlayerItems as you should do) when you play the video clips in the array all the way through, the first time the clip plays through ok, but when it comes to playing that same clip again (as a separate AVPlayerItem) it plays-through very quickly until a certain point in the video then finally starts playing at normal speed from there.
Has anyone else noticed this behaviour?
Apple Developer Support just confirmed to me that AVQueuePlayer does not buffer video items.
I have the same question. I wish AV Foundation has something like asset fully loaded notification.
Following code may partially solve the problem:
Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);
NSArray *times = [NSArray arrayWithObjects:[NSValue valueWithCMTime:secondThird], nil];
self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
}];

MPMoviePlayerController blank frame after seeking to particular time-line

I am developing an iPhone application in which I play a video using MPMoviePlayerController. I use custom controls to play the video.
I have a slider that shows video time line. Using this user can seek the movie to any time-line of the movie.
When user continuously moves the slider:
Pause the video only for first time; [MPMoviePlayerController-obj pause]
MPMoviePlayerController-obj.currentPlaybackTime = slider.value
When slider action ends:
Play the video; [MPMoviePlayerController-obj play]
This plays the movie from the position where user had left the slider. But, it leads to blank frame when movie completes playing. This defect occurs randomly; i.e not for all the seek'd time.
What is the reason for getting the blank frame? How do I solve this?
I'm not sure if this will work, but try setting the initialPlaybackTime to either the slider.value or to currentPlaybackTime.
For being sure that your content is not flawed, hence possibly triggering that issue, you should try to replicate your faulty MPMoviePlayerController results using Apple's reference video content.
HTTP-Streaming: bipbop.m3u8
Progressive Download & Local
Playback: sample_mpeg4.mp4
I have personally observed many issues in connection with improper encoding. Weird things tend to happen when working with lossy compressed content. This is true for video (i-frames vs. p-frames) as well as audio (variable bitrate).
One being improper playback durations being reported. Such issue may result into an unexpected finished-state. I have seen cases where MPMoviePlayerController still shows a bunch of seconds to play even though the actual video has obviously finished. Those cases occur frequently once the user seeks around within the video.
Once you made sure that the issue occurs using the given sample files as well, you should file a bug-report.

Resources