Pause other instances of AVPlayer when playButton is pushed - ios

I have Table View with cells loading video objects from Parse.
When the video is loaded a playButton appears over a still image representation of the video. The play button is an outlet to a storyboard UIButton.
In my FeedCell.m:
- (IBAction)playButtonTapped:(id)sender {
[self.player play];
if (self.player.rate != 0 && (self.player.error == nil)) {
// player is playing
self.playButton.hidden = YES;
}
self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector: #selector (playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.player currentItem]];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
self.playButton.hidden = NO;
[self.playerItem seekToTime:kCMTimeZero];
[self.player pause];
}
I would like to add to the functionality of this button so that when the playButton is tapped the AVPlayerItem will play and all other instances of AVPlayers will be paused.
Am I able to do this by setting up another NSNotification?

NSNotification is not the answer because it sends a broadcast message to all the receiver added as observer,in this case each cell containing the AVPlayers.
But there's a problem: as soon as you scroll the UITableView down, some UITableViewCells go out of screen and they won't receive any NSNotification even if they are added as observers.
So you could add all your AVPlayers to an NSMutableArray and when you want to stop all videos you could cycle the array and manually call the stop method on them.
Then remove them from the array.
Keep in mind that adding AVPlayers to an array cause their reference count to never reach 0 so they are not deallocated while the array is alive.
So put the NSMutableArray on the same controller which contains the UITableView so when it's deallocated,the array is also deallocated and then all AVPlayers are deallocated

Related

iOS pause between songs to exec code then resume, when app is in background

I am building an app which needs to play a track list, but between each song the music should pause to execute some code, then once complete the music should resume. This needs to work when the app is in the background as well as in the foreground.
I have tried a couple of methods but none seem to be able to do everything I want.
AVQueuePlayer - I can't seem to identify when any one song has stopped, only when the whole queue has stopped.
AVPlayer - I can identify when the track has ended with a notification, then I can run my extra code then load the next track. This works fine as long as the app is not in the background, when the app is in the background the code executes fine except the [avPlayer play] command does not work. It does not throw an error, it simply does not play. I know it has moved to the next song and loaded it into AVPlayer as I output the meta data and it has moved on.
Just to be clear the initial track does run in the background, it is only starting the next track which does not run in the background.
Code below...
Any idea what I am doing wrong?
Thanks!
+(void) playItem {
//get the play item from the song array based on intSongIndex
MPMediaItem *currentSong = [songsNowPlaying objectAtIndex:intSongIndex];
AVPlayerItem * currentItem = [AVPlayerItem playerItemWithURL:[currentSong valueForProperty:MPMediaItemPropertyAssetURL]];
[avPlayer replaceCurrentItemWithPlayerItem:currentItem];
//add notification to the currentItem
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:currentItem];
//play
[avPlayer play];
NSArray *metadataList = [[avPlayer currentItem].asset commonMetadata];
for (AVMetadataItem *metaItem in metadataList) {
NSLog(#"%#: %#",[metaItem commonKey], [metaItem value]);
}
//increment song index so next time the next song is selected
intSongIndex ++;
if (intSongIndex >= songsNowPlaying.count) {
intSongIndex = 0;
}
}
+ (void)playerItemDidReachEnd:(NSNotification *)notification {
//add code to be executed before the next song plays
//call playItem to play the next song
[self playItem];
}
Solved, this needed adding to the initial viewDidLoad
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

Detroy AVPlayer Completely

I used AVPlayer to play youtube video in iOS, i created VideoViewController to manage play video.
When VideoViewController pop out NavigationController, i implement
- (void)viewDidDisappear:(BOOL)animated {
[self.player removeObserver:self forKeyPath:kCurrentItemKey context:MyStreamingMovieViewControllerCurrentItemObservationContext];
[self.player removeObserver:self forKeyPath:kRateKey context:MyStreamingMovieViewControllerRateObservationContext];
[self removePlayerTimeObserver];
self.playerLayerView = nil;
self.playerItem = nil;
self.player = nil;
}
but NetworkAcitivtyIndicator still loading in Status Bar, i want stop it. How to fix it?
Did you manage activity indicator by yourself? If so, insert this line in your pop method: [UIApplication sharedApplication].networkActivityIndicatorVisible = NO
Or try to stop player before you set it's reference to nil.
With ARC, objects with a nil value are released at the end of the block or scope in which they are nulled. Although AVPlayer is an object generated by a class cluster (i.e., many different objects), you don't have to release all of them individually,
just the player.
So, this single line will release the player and everything else underneath, including the asset and/or player item:
[self.player replaceCurrentItemWithPlayerItem:nil];
If, for some strange reason, you cannot wait until the end of the method to release the player (perhaps it is preceded by a long-running iterator or is retained by a C-inline block variable), encompass the line in an #autorelease pool:
#autorelease {
[self.player replaceCurrentItemWithPlayerItem:nil];
}

MPMoviePlayerController seek forward button stops the video in IOS7?

I am facing an issue with MPMoviePlayerController in iOS 7. When i single tap on the forward seek button the video stops and not allow to do anything like to play again full screen and slider change.
Here is my code.
remove the Observer for the MPMoviePlayerPlaybackDidFinishNotification
[[NSNotificationCenter defaultCenter] removeObserver:moviePlayerViewController name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayerViewController.moviePlayer];
and add New Notification MPMoviePlayerPlaybackDidFinishNotification
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(videoFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
Here is my custom method to handle the MPMoviePlayerPlaybackDidFinishNotification
-(void)videoFinished:(NSNotification*)aNotification{
MPMoviePlayerController *moviePlayer = [aNotification object];
NSLog(#"%f",moviePlayer.currentPlaybackTime);
int reason = [[[aNotification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
if (reason == MPMovieFinishReasonPlaybackEnded) {
}else if (reason == MPMovieFinishReasonUserExited) {
[self performSelector:#selector(dismiss:) withObject:aNotification afterDelay:0.5];
}else if (reason == MPMovieFinishReasonPlaybackError) {
}
}
I need to stop this strange behaviour on single click and continue to play.
Anyone know how to do this?
Thanks.
I think there are no any notifications or event are available on user
interaction with the standard player buttons, and i have to implement
own UI for the player controls. by this way we can then determine the
actions for a single touch, long touch, etc. Then, we can add whatever
functionality like increasing the play rate, or simply seeking to a
time.

How to stop MPMoviePlayerViewController's automatic dismiss on moviePlaybackDidFinish?

An MPMoviePlayerViewController which is presented modally through presentMoviePlayerViewControllerAnimated: automatically dismisses itself when it's content finishes playing.
I've tried to disable this, since I want to play other content afterwards. However, even if I register to the NSNotificationCenter with [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(movieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:playerVC.moviePlayer]; and set some other content, it still dismisses.
How can I stop MPMoviePlayerViewController from automatically dismissing itself?
UPDATE:
As a clarification, this question is only about removing the automatic dismissal and not about dealing with the disabled 'done' button. The selected answer reflects. This is by design, since we assume the developer adds their own means of dismissing the MPMoviePlayerViewController. However, #bickster's answer deals with the 'done' button as well.
Thanks to this blog article I figured out that MPMoviePlayerViewController automatically registers itself to the NSNotificationCenter upon creation. You have to first remove this registration and it will stop dismissing itself automatically.
// Initialize the movie player view controller with a video URL string
MPMoviePlayerViewController *playerVC = [[[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:aVideoUrl]] autorelease];
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver:playerVC name:MPMoviePlayerPlaybackDidFinishNotification object:playerVC.moviePlayer];
You can use this code to stop the viewcontroller from automatically dismissing and capture the event when the user clicks the "Done" button so you can dismiss the viewcontroller yourself.
Step 1. - alloc a MPMoviePlayerViewController
videoPlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:[[NSURL alloc ]initWithString:[aURL];
Step 2. - Remove the default MPMoviePlayerPlaybackDidFinishNotification observer and add your own
[[NSNotificationCenter defaultCenter] removeObserver:videoPlayer
name:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayer.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(videoFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayer.moviePlayer];
Step 3. - Present viewcontroler
[self presentMoviePlayerViewControllerAnimated:videoPlayer];
Step 4. - Add videoFinish: method
-(void)videoFinished:(NSNotification*)aNotification{
int value = [[aNotification.userInfo valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
if (value == MPMovieFinishReasonUserExited) {
[self dismissMoviePlayerViewControllerAnimated];
}
}
You can try something like this.
when the mpmovieplayercontroller finishes playing a video and you recieve the notification in your method movieFinishedCallback: implemect
[playerVC.movieplayer setContentURL:// set the url of the file you want to play here];
[playerVC.moviePlayer play];
Hope this helps
Since "Done" button is not working anymore if I remove MPMoviePlayerPlaybackDidFinishNotification from NSNotificationCenter, I change repeat mode to MPMovieRepeatModeOne.
Then, everything's working fine except the video is repeated.
MPMoviePlayerViewController *playerVC = [[[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:aVideoUrl]] autorelease];
[playerVC.moviePlayer setRepeatMode:MPMovieRepeatModeOne];

How do you release an MPMoviePlayerViewController after playback finishes?

I am using an MPMoviePlayerViewController to play a video in iOS. When the video stops or crashes, I would like to close this view controller and release it. To simulate the crash, I've passed in a nonexistent file path:
MPMoviePlayerViewController* p = [
[MPMoviePlayerViewController alloc] init
];
self.player = p;
[p release];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.player.moviePlayer
];
self.player.moviePlayer.contentURL = #"purposelyFake.mp4";
This will immediately trigger the following callback.
- (void) moviePlayBackDidFinish
{
if (self.player != nil) {
[self.player.moviePlayer stop];
self.player = nil
}
}
self.player is a nonatomic retained property, so setting it to nil should release it. This clears the memory, but my log shows this warning:
An instance 0x127b20 of class AVPlayerItem was deallocated while key
value observers were still registered with it. Observation info was
leaked, and may even become mistakenly attached to some other object.
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger.
The reason is because moviePlayBackDidFinish is called twice when the video crashes. Once to say MPMovieFinishReasonPlaybackEnded and again to say MPMovieFinishReasonPlaybackError. The MPMoviePlayerViewController becomes deallocated while the second notification is still being sent. My question is: how do I get rid of this warning? Keep in mind that in normal playback finishing, only MPMovieFinishReasonPlaybackEnded is issued, so I can't just hardcode it to only release whenever I receive two notifications in a row.
In your callback method try removing yourself as an observer of the notification before you release it.
[[NSNotificationCenter defaultCenter] removeObserver: self name: MPMoviePlayerPlaybackDidFinishNotification object: myMPMoviePlayerController];

Resources