views:

2370

answers:

3

In my app I am playing video(s) that are in the app, using the standard MPMoviePlayerController class.

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:

*** Terminating app due to uncaught exception 'MPMoviePlayerControllerPlaybackException', reason: 'MPMoviePlayerController instance is already playing'

I can not figure out why this is happening.

I have VERY similar code in another app and I don't get this error.

I am compiling for the device - 2.0 and running it on an iPhone with firmware 2.2.1.

This is the code I have:

@synthesize movieURL;

- (void) setMovieAndPlay
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    moviePath = [[[paths lastObject] stringByAppendingPathComponent:movieURL] retain];

    [self playVideoWithURL:[NSURL fileURLWithPath:moviePath]];
}

-(void)playMovieAtURL:(NSURL*)theURL
{
     MPMoviePlayerController *mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:theURL];
    mMoviePlayer.scalingMode = MPMovieScalingModeAspectFill;

     if ([defaults boolForKey:@"disableControls"])
     {
          mMoviePlayer.movieControlMode = MPMovieControlModeHidden;
     }

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:mMoviePlayer];

    [mMoviePlayer play];
}

- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
     MPMoviePlayerController *theMovie = [notification object];

     [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:theMovie];

     [theMovie release];
     theMovie = nil;

     NSDictionary *notiUserInfo = [notification userInfo];

     if (notiUserInfo != nil)
     {
          NSError *errorInfo = [notiUserInfo objectForKey:@"error"];

          if ([[errorInfo domain] isEqualToString:@"MediaPlayerErrorDomain"])
          {
               UIAlertView *notice = [[UIAlertView alloc] initWithTitle:@"Error" message:[errorInfo localizedDescription] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
               [notice show];
               [notice release];

               return;
          }
     }

     if ([defaults boolForKey:@"autoRepeat"])
     {
          [self playMovieAtURL:[NSURL fileURLWithPath:moviePath]];
     }
     else
     {
          KFAppDelegate *appDelegate = (KFAppDelegate *)[[UIApplication sharedApplication] delegate];
          [appDelegate endMovie];
     }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    KFAppDelegate *appDelegate = (KFAppDelegate *)[[UIApplication sharedApplication] delegate];
    [appDelegate endMovie];
}

What is even stranger is, if you look at the code, after the movie ends I check if the user has enable auto-repeat.

If they have, I just start the movie over again, and THIS WORKS.

However if they did not enable auto-repeat and leave this class and then try to watch another movie (or the same one) it gives that crash.

Does anyone know why this would be happening?

Am I doing something wrong?

Thanks!

A: 

You will get this error only if your MPMoviePlayerController object has not been released and you are trying to play another movie.

Create an instance variable of MPMoviePlayerController and use that through out your code. So instead of

theMovie = [notification object];
[theMovie release];
theMovie = nil;

use an instance variable and use that in playMovieAtURL and playbackDidFinish methods. My problem was that the movie player object was not getting released and so I could not see the next video, only hear the audio playing.

Hopefully that'll help.

lostInTransit
As you can see I am doing that:MPMoviePlayerController *theMovie = [notification object];[theMovie release];theMovie = nil;Its not that I can only hear audio, instead I get a total Crash of the app.Thanks though. Any other thoughts?
kdbdallas
I have also tried it with an instance var that gets released in playbackDidFinish with the same results. I have step through it with debugger and it is getting released.
kdbdallas
In that case can you post the code where you are invoking the playMovieAtURL method
lostInTransit
I added more of the code
kdbdallas
what happens if you write a release statement after the [mMoviePlayer play] method call?
lostInTransit
+1  A: 

Here's my solution. I had to make the next playback wait 1 second using NSTimer or I'd get the same audio-only problem everyone has been talking about. This is for 2.2.1.

- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(playTrack) userInfo:nil repeats:NO];
}

- (void) playTrack {

[moviePlayer stop];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer];
[moviePlayer release];

    NSURL *nsUrl = [NSURL URLWithString:track.url];
    moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:nsUrl];

    // Register to receive a notification when the movie has finished playing. 
    [[NSNotificationCenter defaultCenter] addObserver:self 
               selector:@selector(moviePlayBackDidFinish:) 
                name:MPMoviePlayerPlaybackDidFinishNotification 
                 object:moviePlayer];
    [moviePlayer play];
}
A: 

I think in

- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
    // you should add this:
    if (mMoviePlayer != nil) {
        // free the old movie player
        NSLog(@"releasing!");
        [mMoviePlayer release];
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
}
gene tsai