5

注: 下部の更新を参照してください。


リストからビデオを 1 つずつ再生するアプリケーションがあります。そこで、この機能をテストするために、View Controller が 1 つだけの単純なアプリケーションを作成しました。このView Controllerを実装する前に、このブログを参考にしました。ビュー コントローラーの名前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が 2 回呼び出されることです。上記のコードからわかるように、viewDidLoad(からinitPlayer呼び出されたviewDidLoad) で 1 回だけ登録しました。ログ出力は次のとおりです。

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

ご覧のとおり、終了した再生は 2 回呼び出されます。これにより、1 つのビデオがキューからスキップされます。(実際、問題が発生した元のプロジェクトではnextVideo、ビデオをサーバーから事前にキャッシュし、キャッシュに存在する場合はキャッシュされたビデオへのパスを返します。存在しない場合は、を返しますnil ) ここでは、最初にsintel_trailer.mp4が再生されます。再生終了後、 の代わりに を再生elephants_dream_trailer.mp4しますbig_buck_bunny_trailer.mp4。つまり、その間にスキップしてビデオを循環再生します。MPMoviePlayerPlaybackDidFinishNotificationでは、が 2 回呼び出される原因は何でしょうか? 私はこれに2日間取り組んでいますが、まだうまくいきません。何か案が?

更新 1:

現在、私はmoviePlayBackDidFinish:以下のようなコールバックでスイッチを使用しており、動作しています:

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

それでも、コールバックが 2 回呼び出される原因を知りたいです。スイッチの現在の解決策はハックのように感じ、削除するのが好きです。

更新 2:

今まで、iPhone 4.3シミュレーターで試してきました。しかし、同じプログラムを iPhone 5.0 シミュレーターと iPhone 5.1 シミュレーターで試したところ、問題なく動作しました。つまり、ムービーの再生が終了した後に送信されるコールバックは 1 つだけです。そして、それは私の小さなハック(更新1で)を役に立たなくします(4.3では問題を解決しますが、5.0と5.1では問題を引き起こします). MacOSX Lion - 10.7.4 で実行されている Xcode 4.3.2 を使用しています。この問題を解決する方法について何か考えはありますか? なぜ 4.3 で 2 つのコールバックがあるのですか?

更新 3:

問題の原因となっている行を特定します。方法にありplayNextVideoます。問題の原因となる行は です_moviePlayer.contentURL = _movieUrl;。最初のコールバックで変更すると、 がMPMoviePlayerPlaybackDidFinishNotification再度送信されます。ただし、iPhone 4.3 シミュレータでのみ発生します。何か案が?

更新 4:

それでも、私はこの奇妙な行動について何の考えも持っていません。そのため、UPDATE 1 のようなタイム トリックを次のように使用しています。moviePlayBackDidFinish:

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

2 に答える 2

4

私も同じ問題を抱えていました。そしてこのように解決しました:

動画をスキップする新しいメソッドを作成しました

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

プレーヤーを停止すると、 (シミュレーターとデバイスで) 通知skipVideoが発生します。MPMovieFinishReasonPlaybackEndedプレーヤーの contentUrl を設定すると、他のMPMovieFinishReasonPlaybackEnded通知は発生しないため、moviePlayBackDidFinish一度だけ呼び出されます。

次のビデオ ( 内playNextVideo) を再生する前に、電話する必要があります

[_moviePlayer prepareToPlay];

それは私にとってはうまくいきます!

于 2012-10-22T14:23:09.240 に答える
1

次のトラック用に新しいプレーヤーを作成できます。

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

それ以外の

self.moviePlayer.contentURL = _movieUrl;

通知MPMoviePlayerPlaybackDidFinishNotificationは1回呼び出されます。

于 2012-11-11T08:38:11.290 に答える