注意:请参阅底部的更新。
我有一个应用程序可以从列表中逐个播放视频。因此,为了测试此功能,我创建了一个只有一个视图控制器的简单应用程序。在实现this视图控制器之前,我引用了此博客。视图控制器名为TNViewController
,其实现如下:
#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
)中注册了一次。这是日志输出:
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
会提前从服务器缓存视频,并返回缓存视频的路径(如果缓存中存在)。否则返回{ {1}}。)。在这里,首先播放nil
。完成播放后,它会播放sintel_trailer.mp4
,而不是elephants_dream_trailer.mp4
。也就是说,它循环播放中间跳过的视频。那么,是什么导致big_buck_bunny_trailer.mp4
调用两次?我正在研究这两天,仍然没有运气。有什么想法吗?
更新1:
目前我在回调MPMoviePlayerPlaybackDidFinishNotification
中使用了一个开关,如下所示,正在运行:
moviePlayBackDidFinish:
但我仍然想知道是什么原因导致回调被调用两次。我认为当前的切换解决方案就像黑客一样,并且喜欢删除它。
更新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上有两个回调?
更新3:
我精确指向该行会导致问题。它采用if (!_playNextVideo) {
_playNextVideo = YES;
return;
}
_playNextVideo = NO;
// code to play video....
方法。该行导致的问题是playNextVideo
。在第一次回调中更改它会导致_moviePlayer.contentURL = _movieUrl;
再次发送。但是,它只发生在iPhone 4.3模拟器中。有什么想法吗?
更新4:
尽管如此,我还没有对这种奇怪的行为有所了解。所以,我现在使用像{1}}
中的UPDATE 1中的时间技巧MPMoviePlayerPlaybackDidFinishNotification
答案 0 :(得分:4)
我遇到了同样的问题。并以这种方式解决了:
我创建了一种跳过视频的新方法
- (void) skipVideo {
[_moviePlayer stop];
}
在skipVideo
中停止播放器会导致MPMovieFinishReasonPlaybackEnded
通知(在模拟器和设备上)。现在设置播放器的contentUrl时,不会导致其他MPMovieFinishReasonPlaybackEnded
通知,因此moviePlayBackDidFinish
只会被调用一次;
在播放下一个视频(playNextVideo
)之前,您必须致电
[_moviePlayer prepareToPlay];
这对我来说很好用!
答案 1 :(得分:1)
您可以为下一首曲目创建新播放器:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: _movieUrl];
if (player)
{
[self setMoviePlayer:player];
}
[self.moviePlayer play];
而不是
self.moviePlayer.contentURL = _movieUrl;
通知MPMoviePlayerPlaybackDidFinishNotification
将调用一次。