Crash with MPMoviePlayerViewController after switching app between background and foreground - ios5

My Situation:
I am recording an audio file with the AVAudioRecorder.
To play the record I am using the MPMoviePlayerViewController.
Recording, playing, stopping etc. is working.
My Problem:
After the playback: When I press the home button (bring my app to the background) and open my app again the app crashes.
It happens only when the playback is completed (pressed 'Finish' or let it play till the end). When I press the home button during playing and open the app again everything is fine.
Here my code for the movieplayer: (using the code from here)
- (void)playVideo:(NSString*)aVideoUrl {
MPMoviePlayerViewController *playerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:aVideoUrl]];
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver:playerVC
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
// Register this class as an observer instead
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
// Set the modal transition style of your choice
playerVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
// Present the movie player view controller
//[self presentModalViewController:playerVC animated:YES];
[self presentMoviePlayerViewControllerAnimated:playerVC];
// Start playback
[playerVC.moviePlayer prepareToPlay];
[playerVC.moviePlayer play];
}
- (void)movieFinishedCallback:(NSNotification*)aNotification {
MPMoviePlayerController *moviePlayer = [aNotification object];
// Remove this class from the observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
// Dismiss the view controller
[self dismissModalViewControllerAnimated:YES];
//[self dismissMoviePlayerViewControllerAnimated];
}
I am using iOS5 and ARC.
The class with the code is a normal UIViewController and is also the caller of the method.
It happens only on devices. No crash in the Simulator, but on my iPhone 4 and iPad 1.
I can't find the reason why my app crashes.
Edit:
I am not quite sure right know, but I think I solved my crash.
I believe the problem is not playing the audiofile, or not only. Recording seems to be part of this problem, too.
In my class for recording with AVAudioRecorder I registered this class as the delegate of AVAudioSession:
[[AVAudioSession sharedInstance] setDelegate: self];
I moved this to my "root" class (my "main" class, from where I start everything) and when I implement the protocol methods 'beginInterruption' and 'endInterruption' (know only with a NSLog statement) I can see that 'beginInterruption' is called when I set the app in the background and 'endInterruption' when I start the app again (foreground).
In the stacktrace you can see something like "AudioSessionInterruptionListener"...seems to be part of the delegate methods.
Edit 2:
I tested my app a few times and the crash never happens again. Setting the AVAudioSessionDelegate to my "root" class seems to be the answer.

For me it was necessary to register the AVAudioSession delegate:
[[AVAudioSession sharedInstance] setDelegate: self];
I added this in the "parent" class of my audiorecord handling class.
Also I had to implement beginInterruption and endInterruption of the AVAudioSession delegate.
After this the crash never happened again.

Related

MPMovie player ViewController lag the video play in ios

I want to play play video on current viewcontroller with full screen.
so i am using MPMoviePlayerViewController to play full screen video but problem is that it does not play smooth.
it lag little bit while playing video.
Here is My code which i am using to play full screen video which is store in my documents directory of phone/ipad
-(void)PlayVideo:(NSString *)videoFilePath
{
NSURL *videoURL = [NSURL fileURLWithPath:videoFilePath];
NSLog(#"videoURL: %#", videoURL);
MPMoviePlayerViewController *playerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver:playerVC
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
// Register this class as an observer instead
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
// Set the modal transition style of your choice
playerVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
// Present the movie player view controller
[self presentViewController:playerVC animated:NO completion:nil];
// Start playback
[playerVC.moviePlayer prepareToPlay];
[playerVC.moviePlayer play];
}
- (void)movieFinishedCallback:(NSNotification*)aNotification
{
// Obtain the reason why the movie playback finished
NSNumber *finishReason = [[aNotification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
// Dismiss the view controller ONLY when the reason is not "playback ended"
if ([finishReason intValue] != MPMovieFinishReasonPlaybackEnded)
{
MPMoviePlayerController *moviePlayer = [aNotification object];
// Remove this class from the observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
// Dismiss the view controller
[self dismissViewControllerAnimated:YES completion:Nil];
}
}
Didn't find any issue with the code except in
[[NSNotificationCenter defaultCenter] removeObserver:playerVC
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
removeObserver should be self and not playerVC.
But ya, that will not cause any problem like you have mentioned.
I would suggest you try profiling your app, which might direct you to the code causing the issue. Probably there might be something that you are doing in the background when the video is playing which is causing it to lag.

MPMoviePlayerViewController called from UITabBarController doesn't send notifications in IOS5

I had an app that worked fine in IOS4 but stopped displaying videos in IOS5. Previously I was using MPMoviePlayerController, so I switched to MPMoviePlayerViewController and now the videos display fine in IOS5.
But, now in IOS5, when the Done button is pressed the video stops playing in MPMoviePlayerViewController, but the MPMoviePlayerController no longer calls Notifications.
The hierarchy looks like:
AppDelegate -> UITabBarController -> UITableView -> UIViewController within which the MPMoviePlayerViewController is called. Inside the UIViewController the code looks like:
MPMoviePlayerViewController *tmpMoviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
self.moviePlayer = tmpMoviePlayer;
[self presentMoviePlayerViewControllerAnimated:self.moviePlayer];
NSNotificationCenter *notificationCenter = [ NSNotificationCenter defaultCenter ];
[notificationCenter addObserver:self
selector:#selector(moviePlayerPlaybackDidFinish:)
name:MPMoviePlayerWillExitFullscreenNotification
object:[self.moviePlayer moviePlayer ]];
I've also tried the following notification names and none of them get triggered:
MPMoviePlayerPlaybackDidFinishNotification
MPMoviePlayerPlaybackStateDidChangeNotification
MPMoviePlayerPlaybackDidFinishReasonUserInfoKey
MPMoviePlayerNowPlayingMovieDidChangeNotification
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
I have seen a SO posting where the MPMoviePlayerViewController was called from within a UITabBarController and the view that called presentMoviePlayerViewControllerAnimated wasn't selected correctly, but I haven't been able to get any other view to work for calling presentMoviePlayerViewControllerAnimated.
Any ideas why the notifications are no longer called when the Done button of MPMoviePlayerViewController is pressed?
I did solve this but I can't remember what the fix actually was. But I can show the code that works now:
[ notificationCenter addObserver: self
selector:#selector(moviePlayerPlaybackDidFinish:)
name: MPMoviePlayerPlaybackDidFinishNotification
object: moviePlayer ];
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
self.moviePlayer.shouldAutoplay = NO;
[self.moviePlayer setControlStyle:MPMovieControlStyleFullscreen];
[[self view] addSubview:[self.moviePlayer view]];
[self.moviePlayer setFullscreen:YES animated:YES];
[self.moviePlayer play];

iPhone: UIApplicationWillResignActiveNotification never called

I am playing a video in a view controller. When the user hits the hardware home button and the video is currently playing the app crashes with a EXC_BAD_ACCESS in the simulator.
I read that I should use the applicationWillResignActive message to stop the video from playing which should solve the crashing. So I am trying to register for this notifcation with the notification center, but my selector never gets called. What am I doing wrong?
The following code is in my media player view controller:
- (void) playMedia {
NSURL *mediaUrl = [NSURL fileURLWithPath:tmpFilePath isDirectory:FALSE];
player = [[MPMoviePlayerViewController alloc] initWithContentURL:mediaUrl];
player.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
player.view.frame = self.view.frame;
[self.view addSubview:player.view];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
[player.moviePlayer play];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
// never gets called!
NSLog(#"resign active");
[player.moviePlayer stop];
}
Note that if you have the UIApplicationExitsOnSuspend key set to true in your app's Info.plist, the applicationWillResignActive method is not called when the user hits the home button.
Not sure why that one isnt working for you, but im using
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopAction:) name:UIApplicationDidEnterBackgroundNotification object:nil];
with success in an Audio Player/Recorder.
possibly try implementing
- (void)applicationWillResignActive:(NSNotification *)notification {
}
in the app delegate and see if it calls.

Release MPMoviePlayer when using a tabbarcontroller

I'm using a tabbarcontroller in which one of the views has an MPMoviePlayer. It works fine, except that if I change tab, the movie doesn't stop and keeps playing in the background. Then if I try to tab back to the movie tab, it crashes.
I think the only code I have to release the MPMoviePlayer is when it's finished playing, but I want it to be released when I change views instead. Then if I go back to the Movie tab, we start fresh.
In my .h file have set up as:
import < UIKit/UIKit.h>
import < MediaPlayer/MediaPlayer.h>
#interface SecondViewController : UIViewController {
MPMoviePlayerController *player;
}
#end
and in my .m file have:
- (void)viewDidLoad {
NSString *url = [[NSBundle mainBundle]
pathForResource:#"vid"
ofType:#"m4v"];
player = [[MPMoviePlayerController alloc]
initWithContentURL:[NSURL fileURLWithPath:url]];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
//--called when the movie view and then add it to the View window--
player.view.frame = CGRectMake(10, 10, 300, 300);
[self.view addSubview:player.view];
//--play movie--
[player pause];
[super viewDidLoad];
}
//--called when the movie is done playing--
- (void) movieFinishedCallback:(NSNotification*) aNotification {
MPMoviePlayerController *moviePlayer = [aNotification object];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
[moviePlayer.view removeFromSuperview];
[player release];
}
Any suggestions? Thank you :)
If you really want to release MPMoviePlayer at tab switch, then do it in viewWillDisappear or viewDidDisappear. Now it's left alive at background, as you described. When you come back to tab, you try to create it again.
Difficult to say what would be the exact reason for crash, there seems to be several possibilities. Next time write a "Why did this crash" question with a call stack.
Maybe you could think about just pause/resume, so you wouldn't need to reallocate new moviePlayer every time user changes tabs? Do alloc/release in viewDidLoad and viewDidUnload, but play/pause in viewWillAppear and viewWillDisappear.

Properly displaying and dismissing fullscreen MPMoviePlayerController in iOS 3.2 (iPad)

I'm having lots of trouble displaying a fullscreen movie in my iPad app and then allowing the user to dismiss it with either the Done button or the "un-fullscreen" button on the player controls.
Initially I was using MPMoviePlayerViewController for the movie presentation, but I wasn't receiving the enter/exit fullscreen notifications from its MPMoviePlayerController object, so I switched to doing it myself.
I can make the movie appear fullscreen (although the transition is janky), but when either the "Done" or "un-fullscreen" buttons are pressed, no action is taken by the player. I've posted my code below:
- (void)startPlayingMovieWithURLString:(NSString *)movieURLString {
// I get all of these callbacks **EXCEPT** the "willExitFullScreen:" callback.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullScreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullScreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishPlayback:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[self.moviePlayerController setContentURL:someExistingURL];
// "self" is a UIViewController subclass, and is presented as a "fullscreen" modal view controller from its parent
// I'm setting the movie player's view's frame to take up the full rectangle of my view controller, but really I want the movie to be completely removed when the user presses "done" (that is, removed from the view hierarchy). Not sure when/where to do this.
self.moviePlayerController.view.frame = self.view.frame;
[self.view addSubview:self.moviePlayerController.view];
[self.moviePlayerController setFullscreen:YES animated:YES];
}
And here is the code for my didFinish callback
- (void)didFinishPlayback:(NSNotification *)notification {
// This ends up recursively telling the player that playback ended, thus calling this method, thus…well you get the picture.
// What I'm trying to do here is just make the player go away and show my old UI again.
[self.moviePlayerController setFullscreen:NO animated:YES];
}
So obviously I am doing something wrong but I've been up and down the documentation and I can't figure out how to make the movie just go away. I figured it would be more intuitive than this. What am I doing wrong?
Here are how the events -> notifications work:
User presses 'Done' button
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
User presses 'Leave fullscreen' button on transport
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
Note that playback does not stop
Movie reaches end
MPMoviePlayerPlaybackDidFinishNotification with the MPMoviePlayerPlaybackDidFinishReasonUserInfoKey set to MPMovieFinishReasonPlaybackEnded
If you call setFullscreen:NO animated:YES on your MoviePlayerController instance from this notification, you'll then get the WillExit and DidExit notifications.
Note that you don't get the PlaybackDidFinish notification when the user presses the Done or Leave Fullscreen buttons.
So, typically, if you want to get rid of the MoviePlayer's view, you need to put [self.moviePlayerController.view removeFromSuperview] in the DidExitFullscreen notification handler. WillExitFullscreen is too soon.
Here's my code:
- (void)willEnterFullscreen:(NSNotification*)notification {
NSLog(#"willEnterFullscreen");
}
- (void)enteredFullscreen:(NSNotification*)notification {
NSLog(#"enteredFullscreen");
}
- (void)willExitFullscreen:(NSNotification*)notification {
NSLog(#"willExitFullscreen");
}
- (void)exitedFullscreen:(NSNotification*)notification {
NSLog(#"exitedFullscreen");
[self.movieController.view removeFromSuperview];
self.movieController = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)playbackFinished:(NSNotification*)notification {
NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch ([reason intValue]) {
case MPMovieFinishReasonPlaybackEnded:
NSLog(#"playbackFinished. Reason: Playback Ended");
break;
case MPMovieFinishReasonPlaybackError:
NSLog(#"playbackFinished. Reason: Playback Error");
break;
case MPMovieFinishReasonUserExited:
NSLog(#"playbackFinished. Reason: User Exited");
break;
default:
break;
}
[self.movieController setFullscreen:NO animated:YES];
}
- (void)showMovie {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteredFullscreen:) name:MPMoviePlayerDidEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exitedFullscreen:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
NSURL* movieURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"tron" ofType:#"mov"]];
self.movieController = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
self.movieController.view.frame = self.view.frame;
[self.view addSubview:movieController.view];
[self.movieController setFullscreen:YES animated:YES];
[self.movieController play];
}
Yes. That's great. There are really notifications mentioned above...
However, there are no MPMoviePlayerPlaybackWillFinishNotification somewhy!!!
That's really a problem.
When you call the movie player as modal (no matter which of the following methods used presentViewController/presentModalViewController/presentVideoController), if you defined .fullScreen = YES, it's not expected to call MPMoviePlayerWillExitFullscreenNotification notification at all (obviously, because it's not cosidering we enter/exit from full screen, but only present/dismiss the controller).
But there are really no any notifications that the video is about to finish and close. That's needed (besides any other situations possible) to catch the moment when the transition of dismissing is started. (The transition, of course, starts before the MPMoviePlayerPlaybackDidFinishNotification called). And, at the same time, application:supportedInterfaceOrientationsForWindow: for previously shown controller is called before the notification, and there is no way to say the AppDelegate that our current controller must be shown already in another orientation.
So, since my video is fullscreen and also without any controls shown (this is kind of an intro, so I just until it finishes) my solution was just to have a timer which checks every short tick (0.1 seconds) what is the video current position... and it it's close to the end, then this is the moment for my own notification.