设置contentURL时,在iPhone 4.3模拟器中再次调用MPMoviePlayerPlaybackDidFinishNotification [英] MPMoviePlayerPlaybackDidFinishNotification being called again in iPhone 4.3 simulator when setting contentURL

查看:86
本文介绍了设置contentURL时,在iPhone 4.3模拟器中再次调用MPMoviePlayerPlaybackDidFinishNotification的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:请参阅底部的更新。

我有一个申请表从列表中逐个播放视频。因此,为了测试这个功能,我创建了一个只有一个视图控制器的简单应用程序。我在实现这个视图控制器。视图控制器名为 TNViewController ,其实现如下:

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 follows:

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>

@interface TNViewController : UIViewController {
  @private
    NSMutableArray *_videoArray;
    int _currentVideo;

    MPMoviePlayerController *_moviePlayer;
    NSURL *_movieUrl;
}

@end

它的实现是:

#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 >= _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

现在,我的问题是通知 MPMoviePlayerPlaybackDidFinishNotification 被调用两次。从上面的代码中可以看出,我只在 viewDidLoad 中注册了一次(在 initPlayer 中调用了 viewDidLoad中)。这是日志输出:

Now, my problem is that the notification MPMoviePlayerPlaybackDidFinishNotification is called twice. As you can see from the above code, I have registered for it only once in the viewDidLoad(in initPlayer called from viewDidLoad). Here is the log output:

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

如您所见,播放完成后调用两次。这会导致从队列中跳过一个视频。 (事实上,在出现问题的原始项目中, nextVideo 预先从服务器缓存视频,并返回缓存视频的路径(如果存在)在缓存中。否则,它返回 nil )。在这里,首先播放 sintel_trailer.mp4 。完成播放后,它将播放 big_buck_bunny_trailer.mp4 ,而不是 elephants_dream_trailer.mp4 。也就是说,它循环播放中间跳过的视频。那么,是什么导致 MPMoviePlayerPlaybackDidFinishNotification 调用两次?我正在研究这两天,仍然没有运气。有什么想法?

As you can see, the playback finished is called twice. This causes one video to be skipped from the queue. (In fact, in original project where the problem occures, nextVideo 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 nil.). Here, first the sintel_trailer.mp4 is played. After it finishes playback, instead of elephants_dream_trailer.mp4, it plays big_buck_bunny_trailer.mp4. That is, it cycles plays the videos skipping on in between. So, what is causing the MPMoviePlayerPlaybackDidFinishNotification to invoke twice? I am working on this for two days, still no luck. Any idea?

更新1:

目前我正在使用开关回调 moviePlayBackDidFinish:如下所示正在运作:

Currently I am using a switch in the callback moviePlayBackDidFinish: like below and is working:

if (!_playNextVideo) {
    _playNextVideo = YES;
    return;
}
_playNextVideo = NO;
// code to play video....

但我还是想知道是什么导致回调被调用两次。我觉得当前的开关解决方案就像黑客一样,并希望将其删除。

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.

更新2:

到目前为止,我一直在尝试使用iPhone 4.3模拟器。但是,当我用iPhone 5.0模拟器和iPhone 5.1模拟器尝试相同的程序时,它没有任何问题。也就是说,电影结束播放后只发送一个回调。这使我的小黑客(在更新1上)无用(它解决了4.3中的问题,但在5.0和5.1中产生了问题)。我正在使用在MacOSX Lion上运行的Xcode 4.3.2 - 10.7.4。你对如何解决这个问题有任何想法吗?为什么4.3上有两个回调?

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?

更新3:

我指出了线导致问题。它位于 playNextVideo 方法中。该行导致的问题是 _moviePlayer.contentURL = _movieUrl; 。在第一个回调中更改它会导致再次发送 MPMoviePlayerPlaybackDidFinishNotification 。但是,它只发生在iPhone 4.3模拟器中。有什么想法吗?

I pinpoint to the line causes problem. It is in playNextVideo method. The line causes problem is _moviePlayer.contentURL = _movieUrl;. Changing it in the first callback causes, the MPMoviePlayerPlaybackDidFinishNotification to be sent again. But, it happens only in iPhone 4.3 simulator. Any idea?

更新4:

仍然,我还没有关于这种奇怪行为的任何想法。所以,我现在使用像UPDATE 1中的时间技巧,如下所示 moviePlayBackDidFinish:

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 moviePlayBackDidFinish:

NSTimeInterval currentCallback = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval difference      = currentCallback - _lastCallback;
_lastCallback                  = currentCallback;
if (difference < 5.0) {
    return;
}
// code to play video....


推荐答案

我遇到了同样的问题。并以这种方式解决了:

I had the same problem. and solved it this way:

我创建了一种跳过视频的新方法

I created a new method to skip a video

- (void) skipVideo {
    [_moviePlayer stop];
}

停止播放器 skipVideo 将导致 MPMovieFinishReasonPlaybackEnded 通知(在模拟器和设备上)。当现在设置播放器的contentUrl时,不会导致其他 MPMovieFinishReasonPlaybackEnded 通知,因此 moviePlayBackDidFinish 仅被调用一次;

Stopping the player in skipVideo will cause a MPMovieFinishReasonPlaybackEnded notification (in simulator and on device). When setting contentUrl of player now, no other MPMovieFinishReasonPlaybackEnded notification is caused, so moviePlayBackDidFinish is called only once;

在播放下一个视频之前(在 playNextVideo 中)你必须致电

Before playing next video (in playNextVideo) you have to call

[_moviePlayer prepareToPlay];

这对我来说没问题!

这篇关于设置contentURL时,在iPhone 4.3模拟器中再次调用MPMoviePlayerPlaybackDidFinishNotification的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆