I'm really going crazy with my welcome view controller.
I have a video in background in continuos loop but every solution that I used causes a small pause/flash when the video is finished and loop.
I use two solution: MPMoviePlayerController and AVPlayer from AVFoundation but I got the same result, a small white flash when video is looped for replay.
My MPMoviePlayerController solution (I prefer a fix for this)
- (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];
}
My AVPlayer solution
(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];
}
What's wrong with these implementation? I really try different fixes found on this site but nothing seems to work.
Any suggestions?
Hm.. I might have an idea for the
AVPlayer
-approach.The
AVPlayer
has this method:This means that you can specify WHEN you want the
AVPlayer
to start atkCMTimeZero
. I have not actually used it, but I have an idea for how it can work.You need to know exactly the moment you start the video the first time. I see you have your own
-(void)playerStartPlaying
which is called by thenotificationCenter
ondidBecomeActive
. I suggest using this method for app variations of[player play];
, so putinside
viewDidAppear
instead of[self.avplayer play];
. It might be good enough.If you here manage to find the device's
hostClockTime
, and add the length of the video, you should end up with the exact time when you want it to start from scratch. I am not testing any of this, and I'm typing from head, so you need to understand what I'm doing, and fix it yourself.You'll have to remove the entire
AVPlayerItemDidPlayToEndTimeNotification
-thing. When the video has reached the end, it's already too late. What we're doing now is telling it when to play the second turn when we start the first. We want nothing to happen whendidPlayToEndTime
is fired, we're handling it manually.So, if you understand what I have done above, you'll also notice that the video only will play twice. We tell the video to play, at the same time as we tell it to replay at
time = now+videoLength
. When that replay is done, nothing happens. It simply reaches end. To fix this, you'll need to somehow call-(void)playerStartPlaying
at the same time as thesetRate:time:atHostTime
is executed on theAVPlayer
. I guess you could start anNSTimer
ordispatch_time
and let it execute he method in exactlynextPlay
-amount of time, but that would kinda defeat the purpose of this thing. Maybe not. You could try different stuff out. You probably CAN do this with some success, but I suggest finding a way to register for when the player started from the start. Maybe you can observe therate
or something, I don't know.. If you want to try it with a delayed method, you can try this:Just keep in mind that this is some recursive shit, so even if you pause the video, this will still keep calling after that duration. In that case I suggest making a
BOOL paused;
and cancel the execution of the entireplayerStartPlaying
if it's set toYES
. ..and of course setpaused = YES;
whenever you want to pause, next to wherever you say[player pause];
If this actually works, but you still get flashes, I know there are several ways to improve this. For instance, the
CMTimeAdd()
should probably be using some kind of synchronization-tool to make sure the times add up using the correcttimeScale
etc.I have now spent way too much time writing this, and it might not even work. I have no idea. Good luck and good night.