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];
Related
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
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];
}
I have a video on a screen that must be played with an infinity loop.
So I wrote :
self.player = [AVPlayer playerWithPlayerItem:avItem];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_player currentItem]];
[_player play];
And I fallback method is :
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[_player.currentItem seekToTime:kCMTimeZero];
[_player play];
}
It works well but sometimes it seems that my fallback is not called. The video is freezing at the end and never played again. It's happening randomly ...
Do you have an idea ?
You can just set a boundary time observer.
- (id) setupBoundaryEndWith:(NSArray*)array
{
__weak MY_AVPlayer* weakself = self;
return [self addBoundaryTimeObserverForTimes:array queue:NULL usingBlock:^{
[weakself stopAVPlayerAndLoopOrTriggerNextTrack];
}];
}
[_player currentItem] is null, since you haven't started playing yet. I would suggest either to explicitly add the AVPlayerItem (ideally) or register to notifications after starting the playback (reverse the 2 last lines).
I have not tried the 2-nd solution, so it might not work, if it takes some time to start the playback. If that is the case, I suggest setting an NSTimer to trigger a second later and then register.
I'm playing an audio file in the background when the app is started. The function is called in viewDidLoad.
-(void)playsong
{
NSString *songUrl = [[NSBundle mainBundle] pathForResource:#"chant" ofType:#"m4a"];
NSData *songFile = [NSData dataWithContentsOfFile:songUrl];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithData:songFile error:&error ];
[audioPlayer play];
}
When the user presses the home button, and the song's current position is 10 secs, the song will stop playing.
But when the user opens the app again, the song starts from the same position.
I'd like it to be started from the beginning every time the app is opened.
Plus for memory reasons wouldn't it be better to deallocate the memory?
I tried to call the function from
- (void)viewWillAppear:(BOOL)animated
and then set it to nil in
- (void)viewWillDisappear:(BOOL)animated
but the method - (void)viewWillDisappear:(BOOL)animated isn't getting called.
If viewWillDisappear isn't working for you, try adding an observer in NSNotification center, which calls a method when the application didEnterBackground. Like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(enteredBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
Then add your code into this method:
-(void)enteredBackground:(NSNotification *)notification {
}
Hope this helps!
UPDATE
Okay, so from where you want to acces the audio object in the about us view, try this:
HomeViewController *HomeVC = [HomeViewController alloc] init];
HomeVC.audioPlayer...//then do what you want with it
As long as you have declared the audio object in the home VC then this should work.
Make sure you have imported the homeview controller in one of the about us view controller, files as well.
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];