title: MPMoviePlayerPlaybackDidFinishNotification being called again in iPhone 4.3 simulator when setting contentURL
body: <p><em>NOTE: See the updates at the bottom.</em></p>
  
  <hr />
  
  <p>I have an application to play videos one by one from a list. So, to test this functionality, I created a simple application with only one view controller. I referenced this blog before implementing <a href="http://mobiledevelopertips.com/video/getting-mpmovieplayercontroller-to-cooperate-with-ios4-3-2-ipad-and-earlier-versions-of-iphone-sdk.html" rel="nofollow">this</a> view controller. The view controller is named <code>TNViewController</code> and its implementation is as follows:</p>
  
  <pre><code>#import &lt;UIKit/UIKit.h&gt;
  #import &lt;MediaPlayer/MediaPlayer.h&gt;
  
  @interface TNViewController : UIViewController {
    @private
      NSMutableArray *_videoArray;
      int _currentVideo;
  
      MPMoviePlayerController *_moviePlayer;
      NSURL *_movieUrl;
  }
  
  @end
  </code></pre>
  
  <p>Its implementation is:</p>
  
  <pre><code>#import "TNViewController.h"
  
  @implementation TNViewController
  - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.
  
      [[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
      [self.view setFrame:CGRectMake(0, 0, 480, 320)];
  
      [self initVideos];
      [self initPlayer];
  }
  
  - (void) initVideos {
      _videoArray = [[NSMutableArray alloc] init];
  
      NSString *path = [[NSBundle mainBundle] pathForResource:@"sintel_trailer" ofType:@"mp4" inDirectory:nil];
      [_videoArray addObject:path];
      path = [[NSBundle mainBundle] pathForResource:@"elephants_dream_trailer" ofType:@"mp4" inDirectory:nil];
      [_videoArray addObject:path];
      path = [[NSBundle mainBundle] pathForResource:@"big_buck_bunny_trailer" ofType:@"mp4" inDirectory:nil];
      [_videoArray addObject:path];
  
      _currentVideo = -1;
  }
  
  - (NSString*) nextVideo {
      _currentVideo++;
      if (_currentVideo &gt;= _videoArray.count) {
          _currentVideo = 0;
      }
      return [_videoArray objectAtIndex:_currentVideo];
  }
  
  - (void) initPlayer {
      _moviePlayer = [[MPMoviePlayerController alloc]init];
  
      [self readyPlayer];
      [self.view addSubview:_moviePlayer.view];
  
      // Register to receive a notification when the movie has finished playing. 
      [[NSNotificationCenter defaultCenter] addObserver:self 
                                               selector:@selector(moviePlayBackDidFinish:) 
                                                   name:MPMoviePlayerPlaybackDidFinishNotification 
                                                 object:_moviePlayer];
  }
  
  - (void) readyPlayer {
      _movieUrl    = [NSURL fileURLWithPath:[self nextVideo]];
      [_movieUrl retain];
  
      _moviePlayer.contentURL = _movieUrl;
  
      // For 3.2 devices and above
      if ([_moviePlayer respondsToSelector:@selector(loadState)]) {
          // Set movie player layout
          [_moviePlayer setControlStyle:MPMovieControlStyleNone];
          [_moviePlayer setFullscreen:YES];
  
          // May help to reduce latency
          [_moviePlayer prepareToPlay];
  
          // Register that the load state changed (movie is ready)
          [[NSNotificationCenter defaultCenter] addObserver:self 
                                                   selector:@selector(moviePlayerLoadStateChanged:) 
                                                       name:MPMoviePlayerLoadStateDidChangeNotification 
                                                     object:nil];
      } else {
          // Register to receive a notification when the movie is in memory and ready to play.
          [[NSNotificationCenter defaultCenter] addObserver:self 
                                                   selector:@selector(moviePreloadDidFinish:) 
                                                       name:MPMoviePlayerContentPreloadDidFinishNotification 
                                                     object:nil];
      }
  }
  
  /*---------------------------------------------------------------------------
   * For 3.1.x devices
   *--------------------------------------------------------------------------*/
  - (void) moviePreloadDidFinish:(NSNotification*)notification {
      // Remove observer
      [[NSNotificationCenter  defaultCenter]  removeObserver:self 
                                                        name:MPMoviePlayerContentPreloadDidFinishNotification 
                                                      object:nil];
  
      // Play the movie
      [_moviePlayer play];
  }
  
  /*---------------------------------------------------------------------------
   * For 3.2 and 4.x devices
   *--------------------------------------------------------------------------*/
  - (void) moviePlayerLoadStateChanged:(NSNotification*)notification {
      NSLog(@"moviePlayerLoadStateChanged");
      // Unless state is unknown, start playback
      if ([_moviePlayer loadState] != MPMovieLoadStateUnknown) {
          // Remove observer
          [[NSNotificationCenter defaultCenter]  removeObserver:self
                                                           name:MPMoviePlayerLoadStateDidChangeNotification 
                                                         object:nil];
  
          // Set frame of movie player
          [[_moviePlayer view] setFrame:CGRectMake(0, 0, 480, 320)];
          // Play the movie
          [_moviePlayer play];
      }
  }
  
  - (void) moviePlayBackDidFinish:(NSNotification*)notification {    
      NSLog(@"playback finished...");
      NSLog(@"End Playback Time: %f", _moviePlayer.endPlaybackTime);
      int reason = [[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
      if (reason == MPMovieFinishReasonPlaybackEnded) {
          NSLog(@"Reason: movie finished playing");
      }else if (reason == MPMovieFinishReasonUserExited) {
          NSLog(@"Reason: user hit done button");
      }else if (reason == MPMovieFinishReasonPlaybackError) {
          NSLog(@"Reason: error");
      }
  
      [self playNextVideo];
  }
  
  - (void) playNextVideo {
      NSString *filePath = [self nextVideo];
  
      [_movieUrl release];
      _movieUrl = [NSURL fileURLWithPath:filePath];    
      [_movieUrl retain];
  
      _moviePlayer.contentURL = _movieUrl;
      [_moviePlayer play];
  }
  
  - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
      return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
  }
  
  - (void) dealloc {
      [_moviePlayer release];
      [_movieUrl release];
      [_videoArray release];
  
      [super dealloc];
  }
  
  @end
  </code></pre>
  
  <p>Now, my problem is that the notification <code>MPMoviePlayerPlaybackDidFinishNotification</code> is called twice. As you can see from the above code, I have registered for it only once in the <code>viewDidLoad</code>(in <code>initPlayer</code> called from <code>viewDidLoad</code>). Here is the log output:</p>
  
  <pre><code>2012-07-02 12:29:17.661 DemoApp[1191:ef03] moviePlayerLoadStateChanged
  2012-07-02 12:30:11.470 DemoApp[1191:ef03] playback finished...
  2012-07-02 12:30:11.471 DemoApp[1191:ef03] End Playback Time: -1.000000
  2012-07-02 12:30:11.472 DemoApp[1191:ef03] Reason: movie finished playing
  2012-07-02 12:30:11.474 DemoApp[1191:ef03] playback finished...
  2012-07-02 12:30:11.475 DemoApp[1191:ef03] End Playback Time: -1.000000
  2012-07-02 12:30:11.476 DemoApp[1191:ef03] Reason: movie finished playing
  
  2012-07-02 12:31:03.821 DemoApp[1191:ef03] playback finished...
  2012-07-02 12:31:03.822 DemoApp[1191:ef03] End Playback Time: -1.000000
  2012-07-02 12:31:03.824 DemoApp[1191:ef03] Reason: movie finished playing
  2012-07-02 12:31:03.826 DemoApp[1191:ef03] playback finished...
  2012-07-02 12:31:03.827 DemoApp[1191:ef03] End Playback Time: -1.000000
  2012-07-02 12:31:03.827 DemoApp[1191:ef03] Reason: movie finished playing
  </code></pre>
  
  <p>As you can see, the playback finished is called twice. This causes one video to be skipped from the queue. (<em>In fact, in original project where the problem occures, <code>nextVideo</code> caches a video in advance from the server, and returns the path to the cached video, if it exists in the cache. Otherwise, it returns <code>nil</code>.</em>). Here, first the <code>sintel_trailer.mp4</code> is played. After it finishes playback, instead of <code>elephants_dream_trailer.mp4</code>, it plays <code>big_buck_bunny_trailer.mp4</code>. That is, it cycles plays the videos skipping on in between. So, what is causing the <code>MPMoviePlayerPlaybackDidFinishNotification</code> to invoke twice? I am working on this for two days, still no luck. Any idea?</p>
  
  <p><strong>UPDATE 1:</strong></p>
  
  <p>Currently I am using a switch in the callback <code>moviePlayBackDidFinish:</code> like below and is working:</p>
  
  <pre><code>if (!_playNextVideo) {
      _playNextVideo = YES;
      return;
  }
  _playNextVideo = NO;
  // code to play video....
  </code></pre>
  
  <p>But still I would like to know what causes the callback being called twice. I feel the current solution of switch like a hack, and like to remove it.</p>
  
  <p><strong>UPDATE 2:</strong></p>
  
  <p>Until now, I have been trying this with iPhone 4.3 simulator. But, when I tried the same program with iPhone 5.0 simulator and iPhone 5.1 simulator, it works without any problem. That is, only one callback is being sent after movie finished playing. And that renders my little hack(on update 1) useless (it solves the problem in 4.3 but creates problem in 5.0 and 5.1). I am using Xcode 4.3.2 running on MacOSX Lion - 10.7.4. Do you have any idea on how to solve this problem? Why two callbacks on 4.3?</p>
  
  <p><strong>UPDATE 3:</strong></p>
  
  <p>I pinpoint to the line causes problem. It is in <code>playNextVideo</code> method. The line causes problem is <code>_moviePlayer.contentURL = _movieUrl;</code>. Changing it in the first callback causes, the <code>MPMoviePlayerPlaybackDidFinishNotification</code> to be sent again. But, it happens only in iPhone 4.3 simulator. Any idea?</p>
  
  <p><strong>UPDATE 4:</strong></p>
  
  <p>Still, I haven't got any idea on this weird behavior. So, I am now using a time trick like the one in UPDATE 1 as follows on <code>moviePlayBackDidFinish:</code></p>
  
  <pre><code>NSTimeInterval currentCallback = [NSDate timeIntervalSinceReferenceDate];
  NSTimeInterval difference      = currentCallback - _lastCallback;
  _lastCallback                  = currentCallback;
  if (difference &lt; 5.0) {
      return;
  }
  // code to play video....
  </code></pre>