在iOS中循环视频会导致小暂停/闪烁(MPMoviePlayerController& AVPlayer)

时间:2015-06-16 13:55:20

标签: ios video mpmovieplayercontroller avplayer

我真的对我的欢迎视图控制器发疯了。

我在后续循环中有一个背景视频但是我使用的每个解决方案都会在视频完成并循环时产生一个小的暂停/闪烁。

我使用两个解决方案:来自AVFoundation的 MPMoviePlayerController AVPlayer ,但我得到了相同的结果,当视频循环播放时会出现一个小的白色闪光。

我的MPMoviePlayerController解决方案(我更喜欢修复此问题)

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"welcome_video" withExtension:@"mp4"];

    self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];

    self.moviePlayer.controlStyle = MPMovieControlStyleNone;
    self.moviePlayer.scalingMode = MPMovieScalingModeAspectFill;

    self.moviePlayer.view.frame = self.view.frame;
    [self.view insertSubview:self.moviePlayer.view atIndex:0];

    [self.moviePlayer prepareToPlay];

    // Loop video
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loopVideo) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];
}

- (void)loopVideo
{
    [self.moviePlayer play];
}

我的AVPlayer解决方案

 (void)viewDidLoad
{
    [super viewDidLoad];

    NSError *sessionError = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&sessionError];
    [[AVAudioSession sharedInstance] setActive:YES error:&sessionError];

    //Set up player
    NSURL *movieURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"welcome_video" ofType:@"mp4"]];
    AVAsset *avAsset = [AVAsset assetWithURL:movieURL];
    AVPlayerItem *avPlayerItem =[[AVPlayerItem alloc]initWithAsset:avAsset];
    self.avplayer = [[AVPlayer alloc]initWithPlayerItem:avPlayerItem];
    AVPlayerLayer *avPlayerLayer =[AVPlayerLayer playerLayerWithPlayer:self.avplayer];
    [avPlayerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [avPlayerLayer setFrame:[[UIScreen mainScreen] bounds]];
    [self.movieView.layer addSublayer:avPlayerLayer];

    //Config player
    [self.avplayer seekToTime:kCMTimeZero];
    [self.avplayer setVolume:0.0f];
    [self.avplayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:[self.avplayer currentItem]];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerStartPlaying)
                                                 name:UIApplicationDidBecomeActiveNotification object:nil];

}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self.avplayer pause];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.avplayer play];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}

- (void)playerItemDidReachEnd:(NSNotification *)notification 
{
    AVPlayerItem *p = [notification object];
    [p seekToTime:kCMTimeZero];
}

- (void)playerStartPlaying
{
    [self.avplayer play];
}

这些实施有什么问题?我真的尝试在这个网站上找到不同的修复,但似乎没有任何工作。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

嗯..我可能对AVPlayer - 方法有所了解。

AVPlayer有这种方法:

- (void)setRate:(float)rate
           time:(CMTime)itemTime
     atHostTime:(CMTime)hostClockTime

这意味着您可以指定希望AVPlayerkCMTimeZero开始的时间。我还没有真正使用它,但我知道它是如何工作的。

您需要确切知道第一次启动视频的那一刻。我发现您拥有自己的-(void)playerStartPlaying,由notificationCenter didBecomeActive调用。我建议将此方法用于[player play];的应用变体,因此请添加

[self playerStartPlaying];

viewDidAppear内而不是[self.avplayer play];。这可能已经足够了。

如果您在这里设法找到设备的hostClockTime,并添加视频的长度,您应该最终得到您希望从头开始的确切时间。我没有测试任何这个,而且我是从头开始打字的,所以你需要理解我正在做的事情,并自己解决。

- (void)playerStartPlaying
{
    //The device's hostClockTime. Basically a number indicating how long the device has been powered on.
    CMTime hostClockTime = CMClockGetHostTimeClock;

    //A CMTime indicating when you want the video to play the next time.
    CMTime nextPlay = CMTimeAdd(hostClockTime, self.avplayer.currentItem.duration);
    /* I don't know if that was correct or not, but you'll find out */

    //Start playing if we're not already playing. There might be an avplayer.isPlaying or something, I don't know, this is probably working as well..
    if(self.avplayer.rate != 1.0)
        [self.avplayer play];

    //Tell the player to restart the video at the correct time.
    [self.avplayer setRate:1.0 time:kCMTimeZero atHostTime:nextPlay];
}

您必须删除整个AVPlayerItemDidPlayToEndTimeNotification内容。当视频到达终点时,已经太晚了。我们现在正在做的是告诉它何时开始第二次当我们开始第一次时。我们希望在didPlayToEndTime被解雇时不会发生任何事情,我们会手动处理它。

所以,如果您了解我上面所做的事情,您还会注意到视频只播放两次。我们告诉视频播放,同时我们告诉它在time = now+videoLength重播。当 重播完成后,没有任何反应。它只是达到了目的。要解决此问题,您需要在-(void)playerStartPlaying执行setRate:time:atHostTime的同时以某种方式致电AVPlayer。我想你可以开始NSTimerdispatch_time并让它在nextPlay完全执行方法 - 时间量,但这有点打败了这件事的目的。也许不吧。你可以尝试不同的东西。你可能会取得一些成功,但我建议找到一种方法来注册玩家从一开始就开始的时间。也许你可以观察rate或其他东西,我不知道..如果你想用延迟方法尝试它,你可以试试这个:

double delayInSeconds = CMTimeGetSeconds(self.avplayer.currentItem.duration); //or something
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self playerStartPlaying];
});

请记住,这是一些递归的狗屎,所以即使你暂停视频,这仍然会在该持续时间后继续调用。在这种情况下,我建议制作BOOL paused;并取消整个playerStartPlaying的执行,如果它设置为YES。 ..当然,只要你想暂停,就在paused = YES;

旁边设置[player pause];

如果这确实有效,但你仍然会闪现,我知道有几种方法可以改善这一点。例如,CMTimeAdd()可能应该使用某种同步工具来确保使用正确的timeScale等添加时间。

我现在花了太多时间写这篇文章,它甚至可能都行不通。我不知道。祝你好运,晚安。