MPMoviePlayerPlaybackDidFinishNotification вызывается снова в симуляторе iPhone 4.3 при настройке contentURL
ПРИМЕЧАНИЕ. См. Обновления внизу.
У меня есть приложение для воспроизведения видео по одному из списка. Итак, чтобы проверить эту функциональность, я создал простое приложение с одним контроллером представления. Я ссылался на этот блог до реализации этого контроллера представления. Контроллер вида назван 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
заранее кэширует видео с сервера и возвращает путь к кэшированному видео, если оно существует в кэше. Иначе возвращаетсяnil
,). Здесь сначала sintel_trailer.mp4
играет. После завершения воспроизведения вместо elephants_dream_trailer.mp4
, играет big_buck_bunny_trailer.mp4
, То есть он циклически проигрывает видео, пропуская между ними. Итак, что вызывает MPMoviePlayerPlaybackDidFinishNotification
ссылаться дважды? Я работаю над этим в течение двух дней, до сих пор не повезло. Любая идея?
ОБНОВЛЕНИЕ 1:
В настоящее время я использую переключатель в обратном вызове moviePlayBackDidFinish:
как показано ниже и работает:
if (!_playNextVideo) {
_playNextVideo = YES;
return;
}
_playNextVideo = NO;
// code to play video....
Но все же я хотел бы знать, что вызывает повторный вызов, вызываемый дважды. Я чувствую текущее решение переключения как взломать, и хотел бы удалить его.
ОБНОВЛЕНИЕ 2:
До сих пор я пробовал это с iPhone 4.3 симулятор. Но когда я попробовал одну и ту же программу с симулятором iPhone 5.0 и симулятором iPhone 5.1, она работала без проблем. То есть после завершения воспроизведения фильма отправляется только один обратный вызов. И это делает мой маленький взлом (в обновлении 1) бесполезным (это решает проблему в 4.3, но создает проблему в 5.0 и 5.1). Я использую Xcode 4.3.2, работающий на MacOSX Lion - 10.7.4. У вас есть идеи, как решить эту проблему? Почему два обратных вызова на 4.3?
ОБНОВЛЕНИЕ 3:
Я точно указываю на линию вызывает проблемы. Он находится в playNextVideo
метод. Линия вызывает проблему _moviePlayer.contentURL = _movieUrl;
, Изменение его в первых причинах обратного вызова MPMoviePlayerPlaybackDidFinishNotification
быть отправленным снова. Но это происходит только в симуляторе iPhone 4.3. Любая идея?
ОБНОВЛЕНИЕ 4:
Тем не менее, я не имею никакого представления об этом странном поведении. Итак, я теперь использую трюк времени, как в обновлении 1, как показано на moviePlayBackDidFinish:
NSTimeInterval currentCallback = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval difference = currentCallback - _lastCallback;
_lastCallback = currentCallback;
if (difference < 5.0) {
return;
}
// code to play video....
2 ответа
У меня такая же проблема. и решил это так:
Я создал новый метод, чтобы пропустить видео
- (void) skipVideo {
[_moviePlayer stop];
}
Остановка игрока в skipVideo
вызовет MPMovieFinishReasonPlaybackEnded
уведомление (в симуляторе и на устройстве). При настройке contentUrl плеера сейчас нет другого MPMovieFinishReasonPlaybackEnded
уведомление вызвано, так moviePlayBackDidFinish
вызывается только один раз;
Перед воспроизведением следующего видео (в playNextVideo
) ты должен позвонить
[_moviePlayer prepareToPlay];
Это прекрасно работает для меня!
Вы можете создать нового игрока для следующего трека:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: _movieUrl];
if (player)
{
[self setMoviePlayer:player];
}
[self.moviePlayer play];
Вместо
self.moviePlayer.contentURL = _movieUrl;
уведомление MPMoviePlayerPlaybackDidFinishNotification
будет вызван один раз.