2012-07-02 1 views
5

참고 : 하단의 업데이트를 참조하십시오.콘텐츠를 설정할 때 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 (initPlayerviewDidLoad에서 호출)에 한 번만 등록했습니다. 다음은 로그 출력입니다.

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.... 
+1

AVQueuePlayer를 사용하여 유사한 문제를 해결했습니다. AVQueuePlayer를 사용하면 AVAsset 및 AVPlayerItem을 사용하여 실제로는 잘 처리 된 코드를 처리 할 수 ​​있습니다. – Winston

+0

@Winston 현재, 나는 시간 속임수 (업데이트 된 질문 참조)로 가고있다. 나는'AVQueuePlayer','AVAsset'과'AVPlayerItem'을 살펴 봤는데,이 경우에는 좋은 후보로 보입니다. 그들을 지적 해 주셔서 감사합니다 (나는 그들에 대해 들어 본 적이 없습니다). 불행히도, 그 프로젝트는 끝났고 나는 지금 다른 프로젝트를 진행 중입니다. 하지만, 이제는 새로운 프로젝트에서 AVQueuePlayer를 사용하는 것을 강력하게 고려할 것입니다. – Jomoos

+0

나는 당신에게 새로운 것을 지적 할 수있어서 기쁘다. 나는 당신의 대답에 대해 고마워한다. – Winston

답변

4

에 다음과 같이 그래서, 지금 UPDATE 1의 같은 시간 트릭을 사용하고 있습니다. 그리고이 방법으로 해결 :

난 (시뮬레이터 및 장치)을 MPMovieFinishReasonPlaybackEnded 통지 발생할 skipVideo에서 플레이어 정지 영상

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

를 건너 뛸 수있는 새로운 방법을 만들었다. 플레이어의 contentUrl을 지금 설정하면 다른 MPMovieFinishReasonPlaybackEnded 알림이 발생하지 않으므로 moviePlayBackDidFinish이 한 번만 호출됩니다.

(playNextVideo에서) 다음 비디오를 재생하기 전에 나를 위해 잘 작동

[_moviePlayer prepareToPlay]; 

전화를해야!

1

당신은 다음 트랙에 대한 새로운 선수를 만들 수 있습니다

MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: _movieUrl]; 
if (player) 
{ 
    [self setMoviePlayer:player]; 
} 
[self.moviePlayer play]; 

대신

self.moviePlayer.contentURL = _movieUrl; 

의 알림 MPMoviePlayerPlaybackDidFinishNotification는 한 번만 호출됩니다.