AVPlayer "Cannot Complete Action" - ios

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.

Related

iOS: AVPlayer play won't play sometimes

So I have an app built with a player that plays a video, I have a [player pause] and [player play] in the didBecomeActive and willResignActive methods. Most of the time works fine, but when I open the app, and press the home button and repeat again that process, around the 8th time the video will not play even though I see the play method getting called.
Does anyone have any idea what might be going on?
The app can be in several states that are not foreground. Before playing, check to see if you still have a player, that it still has a player.currentItem, and if it's status is AVPlayerStatusReadyToPlay.
If any of those conditions are not met, then the player and the item must be reinitialized using the code that you used to create it in the first place.
This is a good candidate for a lazy initializer for your player property.

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.

iOS AVAudioPlayer play sound again

Hey I have a couple of AVAudioPlayers containing one sound each. If I press the same button a couple of times, it should repeat the sound from the beginning. If I press another button afterwards, the running sound shall be stopped in order to "make room" for the new one.
The code I am using for that:
-(void) plays:(int)p{ // p is the index of the sound being triggered
if([players[p] isPlaying])
{ // setting the time back to 0 makes
players[p].currentTime = 0.0; // the player automatically play again
}
else
{
[players[p] play]; // if not playing, start playing
}
if(last!=p)
{ // if the last sound is different from the current
[players[last] stop]; // stop the last one
players[last].currentTime = 0.0;} // put its position back to 0
last=p; // set the 'last' variable
}
However, hitting the same button again ends up in a little delay (maybe 20ms) in which no sound is heard. This is the time, the AVAudioPlayer seems to need to "rewind" the track in order to play it again. One Idea to get around this would be to create multiple objects of AVAudioPlayer for each sound but that'd make some awful code! Any ideas on how to make this process quicker?
Thanks, Alex
EDIT: playing 2 different sounds works perfectly fine, I can't hear any delay in between them as I prepareToPlay all the sounds beforehand.
I know how to eliminate the 20ms gap, but first consider if you want to.
Imagine if you jumped immediately from the mid-point of the sound file to the beginning with no gap at all. Better yet, download Audacity and hear how it sounds. Because of the discontinuity, you are going to get an unpleasant crackling or pop sound. Perhaps that 1 fiftieth of a second of silence actually sounds better than immediately restarting.
If you want an uninterrupted audio stream, you're going to have to put away the easy AVAudioPlayer interface and build an AUGraph. Of course, this means learning a complex audio interface and learning all about audio formats. And then you have to figure out what data you're going to stuff into your audio stream.
How would you make your loop sounds nice? You might try fading out at the touch point, and then fading back in. Or you could search for the zero crossings at the beginning and end of your loop (zero crossings are the place where the value of your sound wave is 0. They happen all the time in a mono output, but might be harder to find if your output is stereo.) In the end will this sound nicer than the 20 ms of silence? Get out Audacity and experiment with looping before you enter the long road of learning the AUGraph interace.
To the same song, how about stopping it, then prepareToPlay and play?

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