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

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.

Related

Subclass of MPMoviePlayerViewController doesn't dismiss animated - Objective C

I've subclassed MPMoviePlayerViewController, and method [parentViewController dismissMoviePlayerViewControllerAnimated] doesn't dismiss it animated. Player just disappears. Even without dismissing it manually and just press "Done" button - player disappears without animation. Inside subclassed player I add button to my player view, that's it. Where could be the problem? Here is some code of initialization my player.
MyMovieViewController *playerView = [[[MyMovieViewController alloc] initWithContentURL:url] autorelease];
[playerView setSelectedVideo:video];
[playerView.moviePlayer setAllowsAirPlay:YES];
[playerView.moviePlayer setShouldAutoplay:YES];
[[MenuController sharedInstance].menuView setHidden:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playBackChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:playerView.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:playerView.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playBackChanged:) name:MPMoviePlayerPlaybackDidFinishNotification object:playerView.moviePlayer];
[self presentMoviePlayerViewControllerAnimated:playerView];
It all depends on when and where did you call the dismiss method. Make sure it looks like this:
[self dismissModalViewControllerAnimated: YES];

iOS application is unresponsive after performing [moviePlayer.view removeFromSuperview]

I'm building a very simple iOS iPad app that consists of a view and 4 buttons.
So basically you have in the Storyboard:
->ViewController
->View (this is added just for alignment and position sake, nothing else)
->View
->Button1
->Button2
->Button3
->Button4
When you press a button a movie will play in fullscreen mode, the same for all 4 buttons.
Once the movie is done, either because it finished or the user pressed "done",
[moviePlayer.view removeFromSuperview] is used to remove the movie, and everything returns to the initial state of the app, with the 4 buttons.
this is the code that plays the movie when a button is pressed
- (void) playMovie:(NSString *)fileName
ofType:(NSString *)fileType
{
NSString *filePath=[[NSBundle mainBundle] pathForResource:fileName ofType:fileType];
NSURL *fileUrl=[NSURL fileURLWithPath:filePath];
self.moviePlayer=[[MPMoviePlayerController alloc] initWithContentURL:fileUrl];
[self.view addSubview:self.moviePlayer.view];
self.moviePlayer.fullscreen = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerWillExitFullscreenNotification
object:self.moviePlayer];
[self.moviePlayer prepareToPlay];
[self.moviePlayer play];
}
And this is the code I use to stop and remove the movie:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerWillExitFullscreenNotification
object:player];
if ([player respondsToSelector:#selector(setFullscreen:animated:)])
{
[player stop];
[player setFullscreen:NO animated:YES];
[player.view removeFromSuperview];
}
}
The problem I encounter is this, after [player.view removeFromSuperview]; is performed, I return to the initial view, with no background image (it turns black) and no response from any of the buttons.
if I remove the view that contains the buttons, and add the buttons to the main view, it works as expected.
Sorry if this isn't too clear, I've been through books and lots of websites but don't seem to be able to get my head around this.
Cheers!
It looks like you are removing the "player" from the superview, but what you orginally added was a "movieplayer"...try removing that from the superview.
I changed the MPMoviePlayerController for a MPMoviePlayer*View*Controller and referenced the movie player within it, without using the setFullScreen and everything started working as expected.
So basically adding a view to the superview and the MPMoviePlayerController setFullScreen=YES are no good in this case.

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.

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.

Overlay on top of Streaming MPMoviePlayerController

I went through the example from apple "MoviePlayer on iPhone"
Im trying to overlay on top of the mpmovieplayercontroller,
it works perfectly with video clip that is in bundle,
but it wont work if i stream the video from the url.
the overlay view will just get hide behind the player.
is there a way to bring the overlay view up to front?
MPMoviePlayerController creates its own window and sets that as the key window - you probably know this already from the MoviePlayer sample app.
I don't know why, but there's a delay when the player uses a stream - so the keyWindow you get right after you initialize the player is likely not the player's window, since that seems to get added later.
You can "cheat" and use a timer to get the player window a few seconds later, and add your overlay:
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(addMyOverlay:) userInfo:nil repeats:FALSE]
Or you can listen for the UIWindowDidBecomeKeyNotification event, and do the same:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyWindowChanged:) name:UIWindowDidBecomeKeyNotification object:nil];
Neither option is great (I'd love to know a cleaner way to do this), but it gets the job done.
You can overlay your view when you receive "MPMoviePlayerContentPreloadDidFinishNotification" notification.
Register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
Add overlay view when receiving the notification:
// Notification called when the movie finished preloading.
- (void) moviePreloadDidFinish:(NSNotification*)notification
{
NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count] > 1)
{
// Locate the movie player window
UIWindow *moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
if ([moviePlayerWindow viewWithTag:0x3939] == nil) {
self.videoOverlayView.tag = 0x3939;
[moviePlayerWindow addSubview:self.videoOverlayView];
}
[moviePlayerWindow bringSubviewToFront:self.videoOverlayView];
}
}
A very simple solution:
appDelegate.window.backgroundColor = [UIColor clearColor];
appDelegate.window.windowLevel = 2;
This will keep your app UI on top of the video window.
my post
Previous answer was based on timer. & fixed 5 seconds.
When Movie player begins, a new window is added to application.
Use a timer to check weather a new window is added to your application or not.
When a window ( movie player window ) is added. set notifications.
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
// Register to receive a notification when the movie scaling mode has changed.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieScalingModeDidChange:)
name:MPMoviePlayerScalingModeDidChangeNotification
object:nil];
videoListController.xmlClassVideoList=t;
// here ttttt is a timer declared in .h file
tttttt=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(startMy) userInfo:nil repeats:YES];
}
-(void)startMy{
NSArray *windows = [[UIApplication sharedApplication] windows];
NSLog(#"%i",[windows count]);
// depends on your application window
// it may be 1/2/3
if ([windows count] > 3) {
// Locate the movie player window
[tttttt invalidate];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyWindowChanged:) name:UIWindowDidBecomeKeyNotification object:nil];
}
}