I am using a custom movie player to play videos with MPMoviePlayer as the base, and while the player will enter fullscreen fine when setFullscreen:YES is called, it won't exit fullscreen when setFullscreen:NO is called.
I have tried:
adding the video's view as a subview of a smaller window, and resetting the view's frame
using notifications as I show below (based on what I read in another question about this problem)
Here is the code I am currently using. None of the answers I have seen have solved the problem for me, so any help would be greatly appreciated.
- (IBAction)fullScreenPressed:(id)sender {
if ([self mediaIsVideo]){
if ([self.videoController.moviePlayer isFullscreen]){
[self.videoController.moviePlayer setFullscreen:NO animated:YES];
UIImage *full = [UIImage imageNamed:#"fullscreen-vector-icon.png"];
[self.videoFullscreenButton setImage:full forState:UIControlStateNormal];
}else{
//video is in fullscreen, add notification observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exitFullscreen) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[self.videoController.moviePlayer setFullscreen:YES animated:YES];
UIImage *undoFull = [UIImage imageNamed:#"fullscreen-exit-vector-icon.png"];
[self.videoFullscreenButton setImage:undoFull forState:UIControlStateNormal];
}
}
-(void)exitFullscreen{
if ([self mediaIsVideo]){
NSLog(#"notification selector called");
[self.videoController.moviePlayer setFullscreen:NO animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
}
Related
I'm using MPMoviePlayerViewController to play video. In my app I am playing video(s) that are in the app, using the standard MPMoviePlayerController class. It works fine on iOS 7 and 8
The first time around around this works great, however after watching 1 video if you try and watch something else the app crashes on MPMoviePlayerController's play method with the error:
: CGContextSaveGState: invalid context 0x0
: CGContextClipToRect: invalid context 0x0
: CGContextTranslateCTM: invalid context 0x0
: CGContextDrawShading: invalid context 0x0
: CGContextRestoreGState: invalid context 0x0
*** -[MPMoviePlayerController retain]: message sent to deallocated instance 0x1e5718b0
I can not figure out why this is happening.
Here is my code:
AFPlayerViewController.h
#property (strong, nonatomic) MPMoviePlayerViewController *playerViewController;
- (id)initWithContentURL:(NSString*)movieUrl
andSubtitlePath:(NSString*)subPath
withTypeServer:(NSString*)serverType
withName:(NSString*)name
withEpisodeId:(NSString*)episodeId
withFilmId:(NSString*)filmId
withDuration:(NSInteger)targetDuration
withSeason:(NSString*)seasonId;
AFPlayerViewController.m
#synthesize playerViewController;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (_movieURL) {
self.playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL: _movieURL];
self.playerViewController.moviePlayer.initialPlaybackTime = -1.f;
self.playerViewController.moviePlayer.shouldAutoplay = NO;
[self.playerViewController.moviePlayer setControlStyle:MPMovieControlStyleNone];
[self.playerViewController.moviePlayer setFullscreen:NO animated:YES];
self.playerViewController.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self.playerViewController.view setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:self.playerViewController.view];
[NSLayoutConstraint alightTopBotLeftRight:self.playerViewController.view inView:self.view];
[self.playerViewController.moviePlayer prepareToPlay];
[self.playerViewController.moviePlayer play];
[self receiveNotificationPlayerViewController];
}
}
- (void)receiveNotificationPlayerViewController {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(durationAvailable:)
name:MPMovieDurationAvailableNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(sourceTypeAvailable:)
name:MPMovieSourceTypeAvailableNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(readyForDisplay:)
name:MPMoviePlayerReadyForDisplayDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didFinishAVideo:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.playerViewController.moviePlayer];
}
- (void) durationAvailable: (NSNotification*) notification {
NSLog(#"1");
}
- (void) loadStateDidChange: (NSNotification*) notification {
NSLog(#"2");
if ([self.playerViewController.moviePlayer loadState] != MPMovieLoadStateUnknown) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
}
- (void) playbackStateDidChange: (NSNotification*) notification {
NSLog(#"3");
switch (self.playerViewController.moviePlayer.playbackState) {
case MPMoviePlaybackStateSeekingForward:
case MPMoviePlaybackStateSeekingBackward:
break;
case MPMoviePlaybackStatePaused: {
NSLog(#"pause");
}
break;
case MPMoviePlaybackStateStopped: {
NSLog(#"stop");
}
break;
case MPMoviePlaybackStateInterrupted:{
NSLog(#"interrupted");
}
break;
case MPMoviePlaybackStatePlaying: {
NSLog(#"playing");
}
break;
default:
break;
}
}
- (void) sourceTypeAvailable: (NSNotification*) notification {
NSLog(#"4");
}
- (void) readyForDisplay: (NSNotification*) notification {
NSLog(#"5");
}
- (void)orientationDidChange: (NSNotification*)notification {
NSLog(#"6");
}
- (void)didFinishAVideo:(NSNotification*)notification {
[self removeNotification];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMovieDurationAvailableNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMovieSourceTypeAvailableNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerReadyForDisplayDidChangeNotification
object:self.playerViewController.moviePlayer];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIDeviceOrientationDidChangeNotification
object:self.playerViewController.moviePlayer];
}
AppDelegate.h
#property (strong, nonatomic) AFPlayerViewController *player;
AppDelegate.m
+ (AppDelegate *)shareInstance{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (UIViewController *)currentVisibleController{
id rootController = self.window.rootViewController;
if ([rootController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootController;
return navigationController.topViewController;
}
if ([rootController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabbarController = (UITabBarController *)rootController;
id topViewController = [tabbarController.viewControllers objectAtIndex:tabbarController.selectedIndex];
if ([topViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navi = (UINavigationController *)topViewController;
return navi.topViewController;
}
return topViewController;
}
return self.window.rootViewController;
}
When I'd like to play a video:
[AppDelegate shareInstance].player = [[AFPlayerViewController alloc] initWithContentURL:link
andSubtitlePath:subtitle
withTypeServer:serverType
withName:nameFilm
withEpisodeId:epObject.episodeId
withFilmId:filmId
withDuration:lastDuration
withSeason:epObject.id_season];
[[AppDelegate shareInstance].currentVisibleController presentViewController:[AppDelegate shareInstance].player animated:NO completion:^{
}];
You may well be getting the error because of notifications being sent to a removed controller object as #Bannings says. Nevertheless, I have a couple of other suggestions that will improve your code (and I am fairly sure will remove the error).
Firstly, the documentation says that you should register for the MPMoviePlayerLoadStateDidChangeNotification and use the loadState method to determine when the movie is ready to start playing. I strongly suggest that you do that.
Secondly, you are instantiating a MPMoviePlayerController every time your view appears. I would suggest that you create a single AFPlayerViewController and a single MPMoviePlayerController. Present the same AFPPlayerViewController whenever you wish to play another movie.
When you first instantiate the MPMoviePlayerController, you will need a a URL, for its init method, but subsequent movies can be started using the contentURL property of MPMoviePlayerController
A side advantage of my suggestion is that it will be very easy for a user to pause a movie, go to a different view, and then quickly resume when they return to the movie view.
Have you called the removeNotification if you try and watch something else? try to add viewDidDisappear:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self removeNotification];
}
I think I see the real problem here: you should be using MPMoviePlayerController not MPMoviewPlayerViewController. Make that change, and see if the crash disappears. My suggestion about loadState should be followed too.
I have the following code to play a video
moviePlayer1 =[[MPMoviePlayerController alloc] initWithContentURL:firstMovieURL1];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doneButtonClicked) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[moviePlayer1 view] setFrame: [self.view bounds]]; // frame must match parent view
[self.view addSubview: [moviePlayer1 view]];
[moviePlayer1 play];
-(void)doneButtonClicked
{
NSLog(#"doneButtonClicked ...");
[moviePlayer1 stop];
[moviePlayer1.view removeFromSuperview];
}
If I use the above code my screen looks like this. I have to click on the little arrow icon and then the screen again to show controls (forward/backwards/done button)
so what I did was add this controlStyle to my code. I see all the buttons now as soon as the player is launched but and now my done button doesn't close the window instead it just pauses the movie. What happened?
moviePlayer1.controlStyle=MPMovieControlStyleFullscreen;
[moviePlayer1 play];
This is how you do it. God forbid if apple makes this logic any easy on us.
[[moviePlayer1 view] setFrame: [self.view bounds]]; // frame must match parent view
[self.view addSubview: [moviePlayer1 view]];
moviePlayer1.controlStyle=MPMovieControlStyleFullscreen;
[moviePlayer1 prepareToPlay];
[moviePlayer1 play];
//use this instead
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doneButtonClicked:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
-(void)doneButtonClicked:(NSNotification*)notification
{
NSLog(#"doneButtonClicked ...");
NSNumber *reason = [notification.userInfo objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
if ([reason intValue] == MPMovieFinishReasonUserExited)
{
// done button clicked!
[moviePlayer1 stop];
[moviePlayer1.view removeFromSuperview];
}
}
I am writing an application that has imbedded videos. If I start the application in Landscape mode first and then start the video it looks good. So then I click on Done and then play the video a second time. The second time the video is now shifted down and to the left. When I click on the video the Done button does not show up. I have to switch the simulator to Portrait mode and then back to Landscape mode and then I can click on Done to stop the video. If I always start the video in Portrait mode, and then turn the simulator to landscape it will work. I can do this many times and the video will always line up properly. However if I play the application always in landscape mode, the video will always shift down and to the left for every play after the first time. I went through many of the other mpmovieplayer questions and compared my code to the other code. I am using IOS6 and Storyboard. I have copied my basic MPMOVIEPLAYER code below. I am really stuck here
MoviePlayerViewController.m
- (void) readyPlayer
mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
// For 3.2 devices and above
if ([mp respondsToSelector:#selector(loadState)])
{
// Set movie player layout
[mp setControlStyle:MPMovieControlStyleFullscreen];
[mp setFullscreen:YES];
// May help to reduce latency
[mp prepareToPlay];
// Register that the load state changed (movie is ready)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
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 has exit fullscreen mode.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerDidExitFullscreenNotification
object:nil];
}
/*---------------------------------------------------------------------------
* For 3.2 and 4.x devices
*
*--------------------------------------------------------------------------*/
- (void) moviePlayerLoadStateChanged:(NSNotification*)notification
{
// Unless state is unknown, start playback
if ([mp loadState] != MPMovieLoadStateUnknown)
{
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
// When tapping movie, status bar will appear, it shows up
// in portrait mode by default. Set orientation to `landscape`
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
// Rotate the view for landscape playback
[[self view] setBounds:CGRectMake(0, 0, 480, 320)];
[[self view] setCenter:CGPointMake(160, 240)];
[[self view] setTransform:CGAffineTransformMakeRotation(M_PI / 2)];
// Set frame of movie player
[[mp view] setFrame:CGRectMake(0, 0, 480, 320)];
} else {
// Rotate the view for landscape playback
[[self view] setBounds:CGRectMake(0, 0, 1024, 748)];
[[self view] setCenter:CGPointMake(374, 512)];
[[self view] setTransform:CGAffineTransformMakeRotation(M_PI / 2)];
// Set frame of movie player
[[mp view] setFrame:CGRectMake(0.0, 0.0, 1024.0, 748.0)];
}
// Add movie player as subview
[[self view] addSubview:[mp view]];
// Play the movie
[mp play];
}
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerDidExitFullscreenNotification
object:nil];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self dismissViewControllerAnimated:YES completion:nil];
}
In MPMoviePlayerController, when ever the controls disappear, even the status bar is disappearing with it. Since i want the status bar to appear even when the control disappears, i placed the below code
[[UIApplication sharedApplication] setStatusBarHidden:NO];
But the above code is not making any difference, teh status bar is getting disappeared along with the player controls. How to solve this problem.
Please find the code below, and let me know how to rectify it.
- (void) readyPlayer {
mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
if ([mp respondsToSelector:#selector(loadState)])
{
// Set movie player layout
[mp setControlStyle:MPMovieControlStyleFullscreen];
[mp setFullscreen:YES];
// May help to reduce latency
[mp prepareToPlay];
// Register that the load state changed (movie is ready)
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerLoadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePreloadDidFinish:) name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification
object:nil];
}
// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
- (void) moviePlayerLoadStateChanged:(NSNotification*)notification {
NSLog(#"moviePlayerLoadStateChanged");
// Unless state is unknown, start playback
if ([mp loadState] != MPMovieLoadStateUnknown)
{
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
// Rotate the view for landscape playback
[[self view] setBounds:CGRectMake(0, 0, 768, 1000)];
// Set frame of movieplayer
[[mp view] setFrame:CGRectMake(0, 0, 768, 1000)];
// Add movie player as subview
[[self view] addSubview:[mp view]];
// Play the movie
[mp play];
}
}
- (void) moviePreloadDidFinish:(NSNotification*)notification {
// Remove observer
NSLog(#"moviePreloadDidFinish");
[[NSNotificationCenter defaultCenter] removeObserver:nil
name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification
object:nil];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
// Play the movie
[mp play];
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
NSLog(#"moviePlayBackDidFinish");
[[UIApplication sharedApplication] setStatusBarHidden:NO];
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[self dismissModalViewControllerAnimated:YES];
}
This is a well-known issue of MPMovieControlStyleFullscreen. Simply use the MPMovieControlStyleEmbedded controlStyle and you are good to go.
And, by the way, that is the more adequate controlStyle for embedded usage anyways.
I am writing a simple method to start a streaming video full-screen and letting the user quit pressing the "Done" button. Problem is, I can't remove the MPMediaPlayerController view, or perhaps I am doing it the wrong way.
- (IBAction)playVideoButtonPressed:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://mysite.com/video.mov"];
mp = [[MPMoviePlayerController alloc] initWithContentURL: url];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerPlaybackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
if ([mp respondsToSelector:#selector(setFullscreen:animated:)]) {
mp.controlStyle = MPMovieControlStyleFullscreen;
mp.shouldAutoplay = YES;
[self.view addSubview:mp.view];
[mp.view setTag:3];
[mp setFullscreen:YES animated:YES];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:mp];
[mp play];
}
- (void)moviePlayBackDidFinish:(id)sender {
NSLog(#"Quitting MoviePlayer");
[mp.view removeFromSuperview];
}
The idea is that the MPMediaPlayerController view is called clicking on a button in the app, and it is dismissed by clicking the "Done" video or when the video ends.
I should have used MPMoviePlayerViewController.