MPMoviePlayerViewController called from UITabBarController doesn't send notifications in IOS5 - iphone

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];

Related

Why won't my movie play using MPMoviePlayerController?

I'm trying to get a basic movie to play in an iPhone app; however, I can't seem to get it to work. Here's my entire code:
#import <MediaPlayer/MediaPlayer.h>
#import "ViewController.h"
#implementation ViewController
- (IBAction)playMoviePressed:(id)sender
{
NSURL *url = [NSURL URLWithString:#"http://www.ebookfrenzy.com/ios_book/movie/movie.mov"];
MPMoviePlayerController *moviePlayer =
[[MPMoviePlayerController alloc] initWithContentURL:url];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
moviePlayer.controlStyle = MPMovieControlStyleDefault;
moviePlayer.shouldAutoplay = YES;
[self.view addSubview:moviePlayer.view];
[moviePlayer setFullscreen:YES animated:YES];
[moviePlayer prepareToPlay];
[moviePlayer play];
}
#end
I have one button on screen that calls playMoviePressed when it is tapped. I know that this method is getting called. I have also tried this with .mov and .mp4 local files that I have dragged into xcode.
I got your code to play the video by adding a frame for it:
[moviePlayer.view setFrame:CGRectMake(0, 0, 320, 200)];
However the video was choppy but that could be my internet connection though. Seeing that you're using an IBAction, you're probably using Interface Builder or Storyboard so the only thing that I can think that's preventing it from playing is that you're missing the connection to the IBOutlet.
If you want a full screen mode, you can simply use MPMoviePlayerViewController and do something like:
MPMoviePlayerViewController* viewController = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
[self presentMoviePlayerViewControllerAnimated:viewController];
I figured it out. My problem was really dumb. Because I am not maintaining a strong pointer to my MPMoviePlayerViewController, it gets deallocated as soon as the method ends. A simple #property in the interface solves this.

MovieFinishedCallback notification not firing

I'm trying to create an app that displays an info page once a video stops playing. I've looked at a similar question posted here and tried to implement it but it still doesn't work. I put an NSLog in the movieFinishedCallback method but that never came up so I'm guessing it doesn't even get called. Can someone help me figure it out?
Here's my implementation code...
-(IBAction)playvideo {
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"sample" ofType:#"mov"]];
MPMoviePlayerViewController *playercontroller = [[MPMoviePlayerViewController alloc]
initWithContentURL:url];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:playercontroller];
[self presentMoviePlayerViewControllerAnimated:playercontroller];
playercontroller.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[playercontroller.moviePlayer play];
playercontroller = nil;
}
- (void) movieFinishedCallback:(NSNotification*) notification {
NSLog (#"The video ended");
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
player = nil;
View2 *second =[[View2 alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
}
I think you're probably getting called back when movie is finished, but failing when presenting View2.
Is View2 a subclass of UIViewController? I can't see how you'll get a valid result by passing nil to initWithNibName:. I'd suggest an NSLog in movieFinishedCallback to prove you get there. Then NSLog(#"second = %#", second); My guess is that's nil.
The fix is to load a view controller for real, either using a valid nib or from your storyboard...
// #"MainStoryboard" should be the name of the storyboard containing your VC
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// #"SomeViewController" has to be set on the attributes inspector of the VC
// in the storyboard
SomeViewController *newVC = [storyboard instantiateViewControllerWithIdentifier:#"SomeViewController"];
[self presentModalViewController:newVC animated:YES];
FINALLY made it work! I don't know if it has to do with the fact that MPMoviePlayerViewController doesn't respond to the NSNotificationCenter but I used MPMoviePlayerController instead to display my video, registered the notification for movieFinishedCallback and then used the movieFinishedCallback method to switch views. See this tutorial for video play http://www.techotopia.com/index.php/Video_Playback_from_within_an_iOS_5_iPhone_Application. Only thing is, now the video won't rotate to landscape!

Extremely weird behavior of navigationBar and MPMoviePlayerController. Bug in iOS or my error?

I have a MPMoviePlayerController object that plays a video fullscreen in either portrait or landscape. If I rotate orientation while the video is playing and do the rotation within a few seconds after the video starts playing and the video status bar is visible, when the video ends my navigation bar is perfect. But if I wait until the video status bar disappears a few seconds into the video playing and then rotate orientation, when the video ends my navigationBar is partially hidden behind the status bar, like pushed up.
Have you ever seen something like this?
I am able to recreate this bug easily. I created a new Single View App and simply added a button and a navigation bar. If I rotate orientation while video is playing, tap to enable fullscreen and the video status bar is still visible, when the video finishes, all is good. But, if I wait to rotate after the video status bar disappears, when I rotate and the video finishes, the navigationBar is under the status bar. See image:
iPhone Image
Here is the simple code I am using:
- (void) playMovie {
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource: #"movie" ofType: #"mov"]];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL: url];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(moviePlayBackDidFinish:)
name: MPMoviePlayerPlaybackDidFinishNotification
object: moviePlayer];
moviePlayer.controlStyle = MPMovieControlStyleDefault;
moviePlayer.shouldAutoplay = YES;
[self.view addSubview: moviePlayer.view];
[moviePlayer setFullscreen: YES animated: YES];
- (void) moviePlayBackDidFinish: (NSNotification *) notification
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver: self
name: MPMoviePlayerPlaybackDidFinishNotification
object: player];
if ([player respondsToSelector: #selector(setFullscreen:animated:)])
{
[player.view removeFromSuperview];
}
Here is where I am currently at with the suggestions given below. I must have something wrong because unfortunately I still have the same problem.
Here is the method onPlayerWillExitFullScreen
UIView *view = [[[UIApplication sharedApplication] delegate].window.subviews lastObject];
if (view) {
[view removeFromSuperview];
[[[UIApplication sharedApplication] delegate].window addSubview:view];
}
MPMoviePlayerController *player = [aNotification object];
[[NSNotificationCenter defaultCenter] removeObserver: self
name: MPMoviePlayerWillExitFullscreenNotification
object: player];
and here is my current playMovie method:
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource: #"movie" ofType: #"mov"]];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL: url];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(moviePlayBackDidFinish:)
name: MPMoviePlayerPlaybackDidFinishNotification
object: moviePlayer];
[[NSNotificationCenter defaultCenter]addObserver: self
selector: #selector(onPlayerWillExitFullScreen:)
name: MPMoviePlayerWillExitFullscreenNotification
object: self.moviePlayer];
moviePlayer.controlStyle = MPMovieControlStyleDefault;
moviePlayer.shouldAutoplay = YES;
[self.view addSubview: moviePlayer.view];
[moviePlayer setFullscreen: YES animated: YES];
Ok, so I found this freaking same bug all over my app first in a UIWebView then in a MPMoviePlayerController, I solved this placing this code in my view controller.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self.navigationController setNavigationBarHidden:YES animated:YES];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
Tricky bugs, tricky fixes.
If you listen for the MPMoviePlayerWillExitFullscreenNotification notification, you can force your main views to redraw properly as follows. The 'window' referenced is your application's main UIWindow object.
When MPMoviePlayerController switched to fullscreen, it actually creates a separate UIWindow instance to present the video. By catching the notification as it transitions back, this code will ensure the views you're switching back to correctly realign.
Admittedly, this is not an elegant solution, but it does work every time.
UIView *view = [window.subviews lastObject];
if (view) {
[view removeFromSuperview];
[window addSubview:view];
}
To listen for this notification, you'll need to do something like this, where self.playerController is your MPMoviePlayerController object.
Remember to stop listening for this notification once you release the player though!
// Determine the default notification centre
NSNotificationCenter *centre = [NSNotificationCenter defaultCenter];
// Listen for interesting movie player notifications
[centre addObserver: self
selector: #selector(onPlayerWillExitFullScreen:)
name: MPMoviePlayerWillExitFullscreenNotification
object: self.playerController];
- (void) moviePlayerWillExitFullScreen:(id)sender {
[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:NO];
}
Guys try this... It works for me. I tried many other ways and only this one worked.

Switching view controllers at the end of a movie's playback?

In my app, there's a UIButton that the end user can push, which pushes a view controller that plays a video in a frame using MPMoviePlayerController.
I'd like to be able to detect when the video reaches it's end, and when that happens, push a new view controller. Though I can't seem to find the code that can do this.
Does anyone know how to do this?
The video playback code just looks like this:
//video stuff
CGRect myVideoRect = CGRectMake(0.0f, 145.0f, 320.0f, 160.0f);
movieUrl = [[NSBundle mainBundle] URLForResource:#"myMovie" withExtension:#"m4v"];
moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:movieUrl];
[[moviePlayerController view] setFrame:myVideoRect];
moviePlayerController.controlStyle = MPMovieControlStyleNone;
[self.view addSubview:moviePlayerController.view];
[moviePlayerController play];
Thanks!
Register your view controller to listen to playbackdidfinish notification for the player:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:player];
Then implement a playbackFinished method where you can push a new view controller or perform any other action.

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.