views:

8203

answers:

9

I have a couple of views that access the movie player. I've put the following code in a method in AppDelegate for these views. They send in the filename to play. The code works fine but I know a release is required somewhere. If I add the last line as a release or autorelease, the app will crash once the user presses done on the movieplayer.

MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] 
                 initWithContentURL:[NSURL fileURLWithPath:moviePath]];
moviePlayer.movieControlMode = MPMovieControlModeDefault;
[moviePlayer play];
//[moviePlayer release];

I get this error:

objc[51051]: FREED(id): message videoViewController sent to freed object=0x1069b30

Program received signal: “EXC_BAD_INSTRUCTION”.

How should I be releasing the player?

+5  A: 

What I've found is that the MPMoviePlayerController has to be sent the stop message before you can safely release it. So I do it in handlePlaybackEnd - first I stop it, then I autorelease it. Calling release doesn't seem to work too well:

- (void) moviePlayBackDidFinish : (NSNotification *) notification
{
  VideoPlayerController * player = notification.object;
  [player stop];
  [player autorelease];
}

The whole thing becomes a bit trickier in that the MPMoviePlayerPlaybackDidFinishNotification can get sent more than once, but calling stop/autorlease twice won't do you any good either. So you need to guard against that somehow.

Lastly, it seems to take a few iterations of the main run loop until you can safely create a new MPMoviePlayerController instance. If you do it too quickly, you'll get sound but no video. Great fun, huh?

Yeah -I got the sound/no video thing already. Awesome! How do you guard against multiple notifications? Will it crash if you handle multiple?
4thSpace
You'll have to have some sort of flag: if it's not set, set it and release the player. If it's set, don't do anything.
You could probably also store the MPMoviePlayerController as an ivar in the delegate. In the moviePlaybackDidFinish: method you can release the ivar and set it to nil instead of accessing notification.object. The second notification will send stop and autorelease to nil, which is fine.
Daniel Dickison
Sometimes I would still listen to audio from the video in the background after the user tapped the "Done" button. Autorelease the ivar player (instead of release) did the trick for me.
leolobato
+4  A: 

To answer 4thSpace's comment on the answer above, you can remove the notification observer so you don't receive it multiple times:

- (void)moviePlayBackDidFinish:(NSNotification *)notification {
    MPMoviePlayerController *theMovie = [notification object];
    [[NSNotificationCenter defaultCenter] removeObserver:self
     name:MPMoviePlayerPlaybackDidFinishNotification
     object:theMovie];
    [theMovie stop];
    [theMovie release];
}
davidcann
i have tried this , but my memory consumption does not get down? does that mean memory is not released yet?
Nnp
A: 

Stopping and releasing was not enough for me if the player did not reach to its end.

My solution is setting the "moviePlayer.initialPlaybackTime = -1 " at the "moviePlayBackDidFinish:" before releasing it:

-(void)playMovie: (NSString *)urlString{ movieURL = [NSURL URLWithString:urlString]; moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; moviePlayer.initialPlaybackTime = 0; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayBackDidFinish: ) name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer] ;

moviePlayer.scalingMode = MPMovieScalingModeAspectFit; moviePlayer.movieControlMode = MPMovieControlModeDefault; moviePlayer.backgroundColor = [UIColor blackColor];

[moviePlayer play]; }

-(void)moviePlayBackDidFinish: (NSNotification*)notification{ [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer] ;

moviePlayer.initialPlaybackTime = -1;

[moviePlayer stop]; [moviePlayer release];

}

Tiger
A: 

Hi all, I tried the above suggestions. still i can see a 16MB allocated , in INSTRUMENTS.... Is there anyone got a solution ?

UPDATE:

I found that the above suggestion will release the movieplayer instance. But the related cache is still maintained by framework ,w hich is around 16Mb in my app. I am playing a local movie file. Any suggestions ????

SKG
A: 

I had the same problem and I just realized I set the notification method with object:nil (it was a copy paste).

I was having multiple notifications although I shouldn't have had any notifications at all.

Here is my new notification set up code that fixed all (see the object:moviePlayer):

[[NSNotificationCenter defaultCenter] addObserver:self 
                           selector:@selector(moviePlaybackDidFinish:) 
                           name:MPMoviePlayerPlaybackDidFinishNotification 
                           object:moviePlayer]; 

Hope that helps. Now all my code is working properly.

Koray Balci
+2  A: 

for iphone os 3.2 you need to call [moviePlayer pause]; before calling [moviePlayer stop];

Mohan
thank you for this! Had been going crazy over the issue!
lostInTransit
A: 

Wow thanks for the [moviePlayer pause]

bomber
A: 
moviePlayer.initialPlaybackTime = -1;

Thank You VM!

3gs
A: 

This seemed to reduce the memory significantly. However for IOS 4.1 it seems fine.

-(void) videoFinishedCallback:(NSNotification*)aNotification {

thePlayer = [aNotification object];
[[NSNotificationCenter defaultCenter]
 removeObserver:self
 name:MPMoviePlayerPlaybackDidFinishNotification
 object:thePlayer];

thePlayer.initialPlaybackTime = -1;

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
    [thePlayer pause]; 
#endif

[thePlayer stop];
[thePlayer release];

}

woot586