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.)
Related
i am just playing a video by using MPMoviePlayerController...my code is
-(void)playMovie:(NSURL *)url
{
moviePlayer =
[[MPMoviePlayerController alloc]
initWithContentURL:url];
if (IDIOM==IPAD) {
[moviePlayer.view setFrame:CGRectMake(22,100, 720, 300)];
}
else
{
(IS_IPHONE_5)? [moviePlayer.view setFrame:CGRectMake(22, 70, 280, 150)]:[moviePlayer.view setFrame:CGRectMake(22, 40, 260, 140)];
}
[_scrollView addSubview:moviePlayer.view];
moviePlayer.scalingMode =MPMovieScalingModeFill;
[moviePlayer prepareToPlay];
[moviePlayer play];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerDidEnterFullscreen:) name:MPMoviePlayerDidEnterFullscreenNotification object:Nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerDidExitFullScreen:) name:MPMoviePlayerDidExitFullscreenNotification object:Nil];
}
-(void)moviePlayerDidEnterFullscreen :(id)sender
{
NSLog(#"fullscreen");
[moviePlayer play];
moviePlayer.scalingMode =MPMovieScalingModeFill;
}
- (void) moviePlayerDidExitFullScreen:(id)sender {
NSLog(#"exit full screen");
[moviePlayer play];
moviePlayer.scalingMode =MPMovieScalingModeFill;
}
here when i play initially video will be in "MPMovieScalingModeFill" mode...but my problem is that if i press full screen it shows video on full screen ..when i press exit "full screen" then my video mode goes to "MPMovieScalingModeAspectFit" mode.but i need to be always in "MPMovieScalingModeFill" mode .whats wrong with my code..Please help me...
I believe this will generate the MPMoviePlayerScalingModeDidChangeNotification.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieScalingModeDidChange:)
name:MPMoviePlayerScalingModeDidChangeNotification
object:nil];
Source :Apple Doc
MPMoviePlayerScalingModeDidChangeNotification
Posted when the scaling mode of a movie player has changed. There is no userInfo dictionary.
Scaling mode can change programmatically or by user interaction. To set or retrieve the scaling mode of a movie player, access its scalingMode property. The movie player whose state has changed is available as the object associated with the notification.
First set the ScalingMode to None and then set the ScalingMode to AspectFill
Swift Code :
NSNotificationCenter.defaultCenter().addObserver(self, selector: "moviePlayerExitFullscreen:", name: MPMoviePlayerDidExitFullscreenNotification, object: self.moviePlayer)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "moviePlayerEnterFullscreen:", name: MPMoviePlayerWillEnterFullscreenNotification, object: self.moviePlayer)
func moviePlayerEnterFullscreen (notification : NSNotification)
{
self.moviePlayer.scalingMode = MPMovieScalingMode.None
self.moviePlayer.scalingMode = MPMovieScalingMode.AspectFill
}
func moviePlayerExitFullscreen (notification : NSNotification)
{
self.moviePlayer.scalingMode = MPMovieScalingMode.None
self.moviePlayer.scalingMode = MPMovieScalingMode.AspectFill
}
This isn't the "ideal" solution, but it works! Basically, once you exit full screen the MPMoviePlayerController instance gets all screwed up and resetting the scaling property to MPMovieScalingModeFill won't help no matter where or when you do it (I've tried all sorts of stuff and after an hour gave up). Easiest solution is to remove the MPMoviePlayerController and simply allocate a new instance of MPMoviePlayerController each time full screen is exited (not ideal, but totally works):
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:NO];
if (self.moviePlayer != nil)
[self.moviePlayer.view removeFromSuperview];
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:self.videoURL];
self.moviePlayer.view.frame = CGRectMake(#, #, #, #);
self.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
self.moviePlayer.shouldAutoplay = NO;
[self.moviePlayer setContentURL:self.videoURL];
[self.moviePlayer prepareToPlay];
[self.moviePlayer setScalingMode:MPMovieScalingModeFill];
[self.view addSubview:self.moviePlayer.view];
}
PS: Don't forget to call super on viewDidAppear or suffer all sorts of unforeseeable mayhem (a very common mistake in iOS development)
I set up the movie player like this:
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:#"whatever.mp4"];
self.moviePlayer.controlStyle = MPMovieControlStyleNone;
self.moviePlayer.shouldAutoplay = YES;
[self.moviePlayer prepareToPlay];
self.moviePlayer.repeatMode = MPMovieRepeatModeOne;
self.moviePlayer.view.frame = self.container.bounds;
self.moviePlayer.view.userInteractionEnabled = NO;
[self.container addSubview:self.moviePlayer.view];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(moviePlayBackDidFinish:) name: MPMoviePlayerPlaybackStateDidChangeNotification
object: self.moviePlayer];
The notification is necessary to keep the player looping, since repeatMode is pretty much useless (the video will repeat once or twice, maybe a few times depending on the price of rice in China, then stop). So to keep the video looping you have to do this:
- (void)moviePlayBackDidFinish:(NSNotification *)note {
if (note.object == self.moviePlayer) {
NSInteger reason = [[note.userInfo objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
if (reason == MPMovieFinishReasonPlaybackEnded) {
[self.moviePlayer play];
}
}
}
Now the problem is that I need to be able to pause the video. For some reason a call to
[self.moviePlayer pause];
results in the notification getting fired with reason == MPMovieFinishReasonPlaybackEnded even though the documentation clearly states this:
Constants
MPMovieFinishReasonPlaybackEnded
The end of the movie was reached.
Available in iOS 3.2 and later.
Declared in MPMoviePlayerController.h.
The end of the movie was not reached. I just called pause:. So the notification gets fired and thus the movie gets played again, negating the pause action.
So you can see the problem. How can I successfully pause a looping video?
create a BOOL in your class and initialize it to NO,
#property (nonatomic, assign) BOOL isTriggeredByPause;
before calling
[self.moviePlayer pause];
set its value,
self.isTriggeredByPause = YES;
in your method check for it,
(void)moviePlayBackDidFinish:(NSNotification *)note {
if (note.object == self.moviePlayer) {
NSInteger reason = [[note.userInfo objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
if (reason == MPMovieFinishReasonPlaybackEnded) {
if(!self.isTriggeredByPause)
{
[self.moviePlayer play];
}
}
}
}
Modify value of self.isTriggeredByPause on manual play too, or loop won't work.
I'm playing a video from an MPMoviePlayer (not fullscreen), when I enter fullscreen the video keeps playing and fullscreen is shown.
But when I close fullscreen again, I can only see the black background on the place the 'small' video was playing. It doesnt respond to touches or anything.
I never call stop on my player and there is no viewdiddissapear (or similar) function declared.
The movieplayer also isnt released.
I want the video to continue playing when i close fullscreen. Any thoughts?
**EDIT
It works on an iPad 1, but not on an iPad 2... strange... I need it to function on all though.
Movieplayer initialization:
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
self.moviePlayer = [[MPMoviePlayerController alloc] init];
self.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
self.moviePlayer.contentURL = fileURL;
self.moviePlayer.backgroundView.backgroundColor = [UIColor blackColor];
self.moviePlayer.shouldAutoplay = YES;
[self.moviePlayer.view setFrame:CGRectMake(1024, floorf((self.view.bounds.size.height / 2) - (318 / 2)), 425, 318)];
[self.moviePlayer prepareToPlay];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerDidFinish:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:self.moviePlayer];
[self.view addSubview:self.moviePlayer.view];
[self.view bringSubviewToFront:self.moviePlayer.view];
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationCurveEaseOut
animations:^{
self.moviePlayer.view.transform = CGAffineTransformIdentity;
self.moviePlayer.view.position = CGPointMake(floorf(787 - (self.moviePlayer.view.frame.size.width / 2)),
self.moviePlayer.view.position.y);
}
completion:nil];
Notifications
- (void)MPMoviePlayerLoadStateDidChange:(NSNotification *)notification
{
NSLog(#"Loadstate changed");
if((self.moviePlayer.loadState & MPMovieLoadStatePlaythroughOK) == MPMovieLoadStatePlaythroughOK)
{
[self.moviePlayer play];
}
}
- (void)MPMoviePlayerDidFinish:(NSNotification *)notification
{
MPMovieFinishReason finishReason = (MPMovieFinishReason) [notification.userInfo objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch(finishReason)
{
case MPMovieFinishReasonPlaybackError: NSLog(#"Stopped playback due to error");
case MPMovieFinishReasonPlaybackEnded: NSLog(#"I just quitted");
case MPMovieFinishReasonUserExited: NSLog(#"User quitted");
}
}
I am writing a simple method to start a streaming video full-screen and letting the user quit pressing the "Done" button. Problem is, I can't remove the MPMediaPlayerController view, or perhaps I am doing it the wrong way.
- (IBAction)playVideoButtonPressed:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://mysite.com/video.mov"];
mp = [[MPMoviePlayerController alloc] initWithContentURL: url];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerPlaybackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
if ([mp respondsToSelector:#selector(setFullscreen:animated:)]) {
mp.controlStyle = MPMovieControlStyleFullscreen;
mp.shouldAutoplay = YES;
[self.view addSubview:mp.view];
[mp.view setTag:3];
[mp setFullscreen:YES animated:YES];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:mp];
[mp play];
}
- (void)moviePlayBackDidFinish:(id)sender {
NSLog(#"Quitting MoviePlayer");
[mp.view removeFromSuperview];
}
The idea is that the MPMediaPlayerController view is called clicking on a button in the app, and it is dismissed by clicking the "Done" video or when the video ends.
I should have used MPMoviePlayerViewController.
I am developing an iPad app where I am playing the videos stored within the app itself. I am representing the list of videos in a table view. When I select a row, the video
corresponding to that row plays. But while performing this, sometimes screen goes black , no video is visible but only audio is playing.
I am aware that making the video play in fullscreen mode or using the MPMoviePlayerViewController eliminates this problem. But my requirement is that I don't want to play the movie in fullscreen initially. Please guide me on how this can be achieved.
-(void)playMovie {
MPMoviePlayerController *movieController = [[MPMoviePlayerController alloc] initWithContentURL:movieUrl];
self.moviePalyer = movieController;
[movieController release];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePalyerDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object: self.moviePalyer];
self.moviePalyer.view.frame = CGRectMake(240, 0, 561, 313);
self.moviePalyer.view.backgroundColor = [UIColor clearColor];
[self.moviePalyer prepareToPlay];
[self.view addSubview: self.moviePalyer.view];
[self.moviePalyer play];
}
-(void)moviePalyerDidFinish:(NSNotification*)notification
{
[moviePalyer.view removeFromSuperview];
[moviePalyer stop];
moviePalyer.initialPlaybackTime = -1.0;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePalyer];
[moviePalyer release];
moviePalyer = nil;
}
NOTE: This is on ipad simulator
I have solved the issue. Previously I was creating the MPMoviePlayerController for each of the row selection, and releasing it in the moviedidfinish notification method .But now, instead of creating movieplayercontroller for every row selection, I am reusing the MPMoviePlayerControler object which has been already created.
Following is the Code snippet:
-(void)playMovie
{
if(self.moviePalyer == nil)
{
MPMoviePlayerController *movieController = [[MPMoviePlayerController alloc] initWithContentURL:url];
self.moviePalyer = movieController;
[movieController release];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePalyerDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePalyer];
self.moviePalyer.repeatMode = MPMovieRepeatModeOne;
self.moviePalyer.view.frame = CGRectMake(240, 0, 561, 313);
self.moviePalyer.view.backgroundColor = [UIColor clearColor];
}
else
{
[self.moviePalyer setContentURL:url];
[self stopMovie];
}
[self.view addSubview: self.moviePalyer.view];
[self.moviePalyer play];
}
-(void)stopMovie
{
[self.moviePalyer.view removeFromSuperview];
[self.moviePalyer stop];
self.moviePalyer.initialPlaybackTime = -1.0;
}
I am releasing the moviePlayer object in the dealloc method.
Hope this helps who is having the same issue.