I am trying to figure out why I am getting a flash of black screen after I play a video through once. I figured out that when the load state is MPMovieLoadStatePlayable there is no black screen and the video plays through smoothly. However, after the video plays through once the load state becomes MPMovieLoadStateUnknown. I noticed that when the load state is unknown there is a flash of black screen, probably because it has to first call prepareToPlay.
Here is is how I am setting up my video:
- (void)setVideo:(PFObject *)aVideo {
video = aVideo;
if(self.moviePlayer) {
self.moviePlayer = nil;
}
// Get the profile image
PFUser *user = [self.video objectForKey:kFTPostUserKey];
PFFile *profilePictureSmall = [user objectForKey:kFTUserProfilePicSmallKey];
NSString *authorName = [user objectForKey:kFTUserDisplayNameKey];
// Get the video file
PFFile *videoFile = [video objectForKey:kFTPostVideoKey];
NSURL *url = [NSURL URLWithString:videoFile.url];
self.moviePlayer = [[MPMoviePlayerController alloc] init];
[self.moviePlayer.view setFrame:CGRectMake(0.0f,0.0f,320.0f,320.0f)];
[self.moviePlayer setControlStyle:MPMovieControlStyleNone];
[self.moviePlayer setScalingMode:MPMovieScalingModeAspectFill];
[self.moviePlayer setMovieSourceType:MPMovieSourceTypeFile];
[self.moviePlayer setContentURL:url];
[self.moviePlayer requestThumbnailImagesAtTimes:@[ @0.1f, @1.0f ] timeOption:MPMovieTimeOptionExact];
[self.moviePlayer setShouldAutoplay:NO];
[self.moviePlayer.view setBackgroundColor:[UIColor clearColor]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(movieFinishedCallBack)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayerStateChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:self.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.moviePlayer];
}
Here are the functions:
- (void)didTapVideoPlayButtonAction:(UIButton *)sender{
[self.playButton setHidden:YES];
[self.moviePlayer prepareToPlay];
[self.moviePlayer requestThumbnailImagesAtTimes:@[ @0.1f, @1.0f ] timeOption:MPMovieTimeOptionExact];
[self.moviePlayer play];
}
-(void)movieFinishedCallBack{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.moviePlayer];
}
-(void)loadStateDidChange:(NSNotification *)notification{
//NSLog(@"loadStateDidChange: %@",notification);
if (self.moviePlayer.loadState == MPMovieLoadStatePlayable) {
NSLog(@"loadState... MPMovieLoadStatePlayable");
}
if (self.moviePlayer.loadState == MPMovieLoadStatePlaythroughOK) {
NSLog(@"loadState... MPMovieLoadStatePlaythroughOK");
}
if (self.moviePlayer.loadState == MPMovieLoadStateStalled) {
NSLog(@"loadState... MPMovieLoadStateStalled");
}
if (self.moviePlayer.loadState == MPMovieLoadStateUnknown) {
NSLog(@"loadState... MPMovieLoadStateUnknown");
//[self.moviePlayer prepareToPlay];
}
}
-(void)moviePlayerStateChange:(NSNotification *)notification{
//NSLog(@"moviePlayerStateChange: %@",notification);
if (self.moviePlayer.playbackState == MPMoviePlaybackStatePlaying){
[self.imageView addSubview:self.moviePlayer.view];
}
if (self.moviePlayer.playbackState == MPMoviePlaybackStateStopped){
NSLog(@"moviePlayer... Stopped");
[self.playButton setHidden:NO];
//[self.moviePlayer.view removeFromSuperview];
[self.moviePlayer prepareToPlay];
}
if (self.moviePlayer.playbackState == MPMoviePlaybackStatePaused){
NSLog(@"moviePlayer... Paused");
[self.moviePlayer stop];
}
f (self.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted){
NSLog(@"moviePlayer... Interrupted");
[self.moviePlayer stop];
}
if (self.moviePlayer.playbackState == MPMoviePlaybackStateSeekingForward){
NSLog(@"moviePlayer... Forward");
}
if (self.moviePlayer.playbackState == MPMoviePlaybackStateSeekingBackward){
NSLog(@"moviePlayer... Backward");
}
}
I think that the solution would be, calling prepareToPlay from within the statement: if (self.moviePlayer.loadState == MPMovieLoadStateUnknown)
I tried this but it did not prepare the player for play. Any thoughts/advice/help?
The fact that the video can play once but then can't play again later is not some built-in feature of MPMoviePlayerController — I've never had an MPMoviePlayerController of mine behave like that — so you should look for something else that might be happening so as to wreck the state of your MPMoviePlayerController, such as changing its contentURL
to a bad value (which is usually the cause of MPLoadStateUnknown
. Two things come to mind:
I notice that the method where you configure your MPMoviePlayerController is no ordinary method; it is, in fact, a setter for a property (video
). This could be involved in the problem, because it means that every time any code anywhere says video = //...
you are running this method, which means tearing down the entire movie player and making a new one. Now suppose some code somewhere else says video = nil
or some other bad value. That would certainly make your movie player view go black and prevent further playing. I suggest breakpointing this method to see if that's happening. And then set up a better architecture: A setter should just set the property and no more. The configuration of your movie player should happen just once and needs to happen in a method of its own.
Watch out for a situation where you accidentally generate more than on MPMoviePlayerController. There is a law that "There Can Be Only One" - if another MPMoviePlayerController comes along and calls prepareToPlay
, all other MPMoviePlayerControllers are disabled.
(Finally, although this is not the direct source of your problem, note that you are determining the load state incorrectly. You are using equality, but this can easily fail, because the load state is a bit mask; you have to use logical-and to compare masks.)