참고 : 하단의 업데이트를 참조하십시오.콘텐츠를 설정할 때 iPhone 4.3 시뮬레이터에서 MPMoviePlayerPlaybackDidFinishNotification이 다시 호출됩니다.
나는 목록에서 하나의 동영상 하나를 재생하는 응용 프로그램이 있습니다. 따라서이 기능을 테스트하기 위해 뷰 컨트롤러가 하나 뿐인 간단한 애플리케이션을 만들었습니다. 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
위에서 보듯이 재생이 완료되면 두 번 호출됩니다. 이렇게하면 한 비디오가 대기열에서 건너 뜁니다. (실제로 문제가 발생한 원래 프로젝트에서 서버에서 비디오를 미리 캐시하고 캐시에있는 경우 캐시 된 비디오에 대한 경로를 반환합니다. 그렇지 않으면 .을 반환합니다. 여기에서 먼저 sintel_trailer.mp4
이 재생됩니다. 재생이 끝나면 elephants_dream_trailer.mp4
대신 big_buck_bunny_trailer.mp4
이 재생됩니다. 즉, 건너 뛰는 동영상을 순환 재생합니다. 따라서 MPMoviePlayerPlaybackDidFinishNotification
이 두 번 호출하게하는 원인은 무엇입니까? 나는 이틀 동안이 일을하고 있는데, 아직도 운이 없다. 어떤 생각?
UPDATE 1 :
if (!_playNextVideo) {
_playNextVideo = YES;
return;
}
_playNextVideo = NO;
// code to play video....
을하지만 여전히 내가 콜백 두 번 호출되는 원인을 알고 싶습니다 : 현재 나는 아래와 같이 콜백 moviePlayBackDidFinish:
에있는 스위치를 사용하고 있고 작동
. 나는 해킹과 같은 스위치의 현재 해결책을 느끼고 그것을 제거하는 것을 좋아한다.
업데이트 2 : 지금까지
, 나는 아이폰 4.3 시뮬레이터이를 시도하고있다. 하지만, 아이폰 5.0 시뮬레이터와 아이폰 5.1 시뮬레이터로 같은 프로그램을 시도했을 때 아무 문제없이 작동합니다. 즉, 영화 재생이 끝난 후 하나의 콜백 만 전송됩니다. 그리고 그것은 내 작은 해킹 (업데이트 1)을 쓸모 없게 만든다 (4.3의 문제를 해결하지만 5.0과 5.1에서는 문제를 만든다). MacOSX Lion - 10.7.4에서 실행되는 Xcode 4.3.2를 사용하고 있습니다. 이 문제를 해결하는 방법에 대한 아이디어가 있습니까? 4.3의 두 콜백이 필요한 이유는 무엇입니까?
UPDATE 3 :
나는 라인에 정확하게이 문제가 발생합니다. 방법은 playNextVideo
입니다. 문제의 원인은 _moviePlayer.contentURL = _movieUrl;
입니다. 첫 번째 콜백에서 변경하면 MPMoviePlayerPlaybackDidFinishNotification
이 다시 전송됩니다. 그러나 iPhone 4.3 시뮬레이터에서만 발생합니다. 어떤 생각?
UPDATE 4 :
아직도,이 이상한 행동에 어떤 생각이 없어요. 저도 같은 문제가 있었다 moviePlayBackDidFinish:
NSTimeInterval currentCallback = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval difference = currentCallback - _lastCallback;
_lastCallback = currentCallback;
if (difference < 5.0) {
return;
}
// code to play video....
AVQueuePlayer를 사용하여 유사한 문제를 해결했습니다. AVQueuePlayer를 사용하면 AVAsset 및 AVPlayerItem을 사용하여 실제로는 잘 처리 된 코드를 처리 할 수 있습니다. – Winston
@Winston 현재, 나는 시간 속임수 (업데이트 된 질문 참조)로 가고있다. 나는'AVQueuePlayer','AVAsset'과'AVPlayerItem'을 살펴 봤는데,이 경우에는 좋은 후보로 보입니다. 그들을 지적 해 주셔서 감사합니다 (나는 그들에 대해 들어 본 적이 없습니다). 불행히도, 그 프로젝트는 끝났고 나는 지금 다른 프로젝트를 진행 중입니다. 하지만, 이제는 새로운 프로젝트에서 AVQueuePlayer를 사용하는 것을 강력하게 고려할 것입니다. – Jomoos
나는 당신에게 새로운 것을 지적 할 수있어서 기쁘다. 나는 당신의 대답에 대해 고마워한다. – Winston