{"id":6237,"date":"2014-04-14T03:21:50","date_gmt":"2014-04-14T03:21:50","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2014\/04\/14\/mpmovieplayerplaybackdidfinishnotification-being-called-again-in-iphone-4-3-simulator-when-setting-contenturl-collection-of-common-programming-errors\/"},"modified":"2014-04-14T03:21:50","modified_gmt":"2014-04-14T03:21:50","slug":"mpmovieplayerplaybackdidfinishnotification-being-called-again-in-iphone-4-3-simulator-when-setting-contenturl-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2014\/04\/14\/mpmovieplayerplaybackdidfinishnotification-being-called-again-in-iphone-4-3-simulator-when-setting-contenturl-collection-of-common-programming-errors\/","title":{"rendered":"MPMoviePlayerPlaybackDidFinishNotification being called again in iPhone 4.3 simulator when setting contentURL-Collection of common programming errors"},"content":{"rendered":"<p><em>NOTE: See the updates at the bottom.<\/em><\/p>\n<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 this view controller. The view controller is named <code>TNViewController<\/code> and its implementation is as follows:<\/p>\n<pre><code>#import \n#import \n\n@interface TNViewController : UIViewController {\n  @private\n    NSMutableArray *_videoArray;\n    int _currentVideo;\n\n    MPMoviePlayerController *_moviePlayer;\n    NSURL *_movieUrl;\n}\n\n@end\n<\/code><\/pre>\n<p>Its implementation is:<\/p>\n<pre><code>#import \"TNViewController.h\"\n\n@implementation TNViewController\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    \/\/ Do any additional setup after loading the view, typically from a nib.\n\n    [[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];\n    [self.view setFrame:CGRectMake(0, 0, 480, 320)];\n\n    [self initVideos];\n    [self initPlayer];\n}\n\n- (void) initVideos {\n    _videoArray = [[NSMutableArray alloc] init];\n\n    NSString *path = [[NSBundle mainBundle] pathForResource:@\"sintel_trailer\" ofType:@\"mp4\" inDirectory:nil];\n    [_videoArray addObject:path];\n    path = [[NSBundle mainBundle] pathForResource:@\"elephants_dream_trailer\" ofType:@\"mp4\" inDirectory:nil];\n    [_videoArray addObject:path];\n    path = [[NSBundle mainBundle] pathForResource:@\"big_buck_bunny_trailer\" ofType:@\"mp4\" inDirectory:nil];\n    [_videoArray addObject:path];\n\n    _currentVideo = -1;\n}\n\n- (NSString*) nextVideo {\n    _currentVideo++;\n    if (_currentVideo &gt;= _videoArray.count) {\n        _currentVideo = 0;\n    }\n    return [_videoArray objectAtIndex:_currentVideo];\n}\n\n- (void) initPlayer {\n    _moviePlayer = [[MPMoviePlayerController alloc]init];\n\n    [self readyPlayer];\n    [self.view addSubview:_moviePlayer.view];\n\n    \/\/ Register to receive a notification when the movie has finished playing. \n    [[NSNotificationCenter defaultCenter] addObserver:self \n                                             selector:@selector(moviePlayBackDidFinish:) \n                                                 name:MPMoviePlayerPlaybackDidFinishNotification \n                                               object:_moviePlayer];\n}\n\n- (void) readyPlayer {\n    _movieUrl    = [NSURL fileURLWithPath:[self nextVideo]];\n    [_movieUrl retain];\n\n    _moviePlayer.contentURL = _movieUrl;\n\n    \/\/ For 3.2 devices and above\n    if ([_moviePlayer respondsToSelector:@selector(loadState)]) {\n        \/\/ Set movie player layout\n        [_moviePlayer setControlStyle:MPMovieControlStyleNone];\n        [_moviePlayer setFullscreen:YES];\n\n        \/\/ May help to reduce latency\n        [_moviePlayer prepareToPlay];\n\n        \/\/ Register that the load state changed (movie is ready)\n        [[NSNotificationCenter defaultCenter] addObserver:self \n                                                 selector:@selector(moviePlayerLoadStateChanged:) \n                                                     name:MPMoviePlayerLoadStateDidChangeNotification \n                                                   object:nil];\n    } else {\n        \/\/ Register to receive a notification when the movie is in memory and ready to play.\n        [[NSNotificationCenter defaultCenter] addObserver:self \n                                                 selector:@selector(moviePreloadDidFinish:) \n                                                     name:MPMoviePlayerContentPreloadDidFinishNotification \n                                                   object:nil];\n    }\n}\n\n\/*---------------------------------------------------------------------------\n * For 3.1.x devices\n *--------------------------------------------------------------------------*\/\n- (void) moviePreloadDidFinish:(NSNotification*)notification {\n    \/\/ Remove observer\n    [[NSNotificationCenter  defaultCenter]  removeObserver:self \n                                                      name:MPMoviePlayerContentPreloadDidFinishNotification \n                                                    object:nil];\n\n    \/\/ Play the movie\n    [_moviePlayer play];\n}\n\n\/*---------------------------------------------------------------------------\n * For 3.2 and 4.x devices\n *--------------------------------------------------------------------------*\/\n- (void) moviePlayerLoadStateChanged:(NSNotification*)notification {\n    NSLog(@\"moviePlayerLoadStateChanged\");\n    \/\/ Unless state is unknown, start playback\n    if ([_moviePlayer loadState] != MPMovieLoadStateUnknown) {\n        \/\/ Remove observer\n        [[NSNotificationCenter defaultCenter]  removeObserver:self\n                                                         name:MPMoviePlayerLoadStateDidChangeNotification \n                                                       object:nil];\n\n        \/\/ Set frame of movie player\n        [[_moviePlayer view] setFrame:CGRectMake(0, 0, 480, 320)];\n        \/\/ Play the movie\n        [_moviePlayer play];\n    }\n}\n\n- (void) moviePlayBackDidFinish:(NSNotification*)notification {    \n    NSLog(@\"playback finished...\");\n    NSLog(@\"End Playback Time: %f\", _moviePlayer.endPlaybackTime);\n    int reason = [[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];\n    if (reason == MPMovieFinishReasonPlaybackEnded) {\n        NSLog(@\"Reason: movie finished playing\");\n    }else if (reason == MPMovieFinishReasonUserExited) {\n        NSLog(@\"Reason: user hit done button\");\n    }else if (reason == MPMovieFinishReasonPlaybackError) {\n        NSLog(@\"Reason: error\");\n    }\n\n    [self playNextVideo];\n}\n\n- (void) playNextVideo {\n    NSString *filePath = [self nextVideo];\n\n    [_movieUrl release];\n    _movieUrl = [NSURL fileURLWithPath:filePath];    \n    [_movieUrl retain];\n\n    _moviePlayer.contentURL = _movieUrl;\n    [_moviePlayer play];\n}\n\n- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {\n    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);\n}\n\n- (void) dealloc {\n    [_moviePlayer release];\n    [_movieUrl release];\n    [_videoArray release];\n\n    [super dealloc];\n}\n\n@end\n<\/code><\/pre>\n<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>\n<pre><code>2012-07-02 12:29:17.661 DemoApp[1191:ef03] moviePlayerLoadStateChanged\n2012-07-02 12:30:11.470 DemoApp[1191:ef03] playback finished...\n2012-07-02 12:30:11.471 DemoApp[1191:ef03] End Playback Time: -1.000000\n2012-07-02 12:30:11.472 DemoApp[1191:ef03] Reason: movie finished playing\n2012-07-02 12:30:11.474 DemoApp[1191:ef03] playback finished...\n2012-07-02 12:30:11.475 DemoApp[1191:ef03] End Playback Time: -1.000000\n2012-07-02 12:30:11.476 DemoApp[1191:ef03] Reason: movie finished playing\n\n2012-07-02 12:31:03.821 DemoApp[1191:ef03] playback finished...\n2012-07-02 12:31:03.822 DemoApp[1191:ef03] End Playback Time: -1.000000\n2012-07-02 12:31:03.824 DemoApp[1191:ef03] Reason: movie finished playing\n2012-07-02 12:31:03.826 DemoApp[1191:ef03] playback finished...\n2012-07-02 12:31:03.827 DemoApp[1191:ef03] End Playback Time: -1.000000\n2012-07-02 12:31:03.827 DemoApp[1191:ef03] Reason: movie finished playing\n<\/code><\/pre>\n<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>\n<p><strong>UPDATE 1:<\/strong><\/p>\n<p>Currently I am using a switch in the callback <code>moviePlayBackDidFinish:<\/code> like below and is working:<\/p>\n<pre><code>if (!_playNextVideo) {\n    _playNextVideo = YES;\n    return;\n}\n_playNextVideo = NO;\n\/\/ code to play video....\n<\/code><\/pre>\n<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>\n<p><strong>UPDATE 2:<\/strong><\/p>\n<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 &#8211; 10.7.4. Do you have any idea on how to solve this problem? Why two callbacks on 4.3?<\/p>\n<p><strong>UPDATE 3:<\/strong><\/p>\n<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>\n<p><strong>UPDATE 4:<\/strong><\/p>\n<p>Still, I haven&#8217;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>\n<pre><code>NSTimeInterval currentCallback = [NSDate timeIntervalSinceReferenceDate];\nNSTimeInterval difference      = currentCallback - _lastCallback;\n_lastCallback                  = currentCallback;\nif (difference &lt; 5.0) {\n    return;\n}\n\/\/ code to play video....\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>NOTE: See the updates at the bottom. 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 this view controller. The view controller is named TNViewController and its implementation is as [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-6237","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/6237","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=6237"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/6237\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=6237"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=6237"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=6237"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}