How to get iphone movie player handler (UIWindow handler) - iphone

I am trying to play a video like this
self.webView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: urlToMovie];
[self.webView loadRequest: request];
[request release];
could anyone tell me how can i get moviePlayerWindow (UIWindow ) handler if i am playing with the help of safari.
Is there any way to get movie notifications? like did finish loading, finish playing etc.
thank you for your responses :)

NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count] > 1) { // Locate the movie player window
UIWindow * moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
// ... and now you can add views and play around with the window directly
}
There is only one notification you can get at.
http://www.iphonedevsdk.com/forum/iphone-sdk-development/26322-all-mpmovieplayercontroller-notifications.html

Skip the UIWebView and use MPMoviePlayerController directly, which provides the following notifications:
MPMoviePlayerContentPreloadDidFinishNotification
Notifies observers that the movie is now in memory and ready to play. The affected movie player is stored in the object parameter of the notification. If an error occurred during loading, the userInfo dictionary of this notification contains a key with the name “error” whose value is the NSError object describing the problem.
MPMoviePlayerPlaybackDidFinishNotification
Notifies observers that the movie finished playing. The affected movie player is stored in the object parameter of the notification.
MPMoviePlayerScalingModeDidChangeNotification
Notifies observers that the scaling mode property of the player changed. The affected movie player is stored in the object parameter of the notification.
User actions may also cause the media player to send this notification.

Related

Asynchronous MPMoviePlayerController load has no duration value

I assume that the initWithContentURL: method is not asynchronous. I added the following methods to do the load in the background and then assign the temporary player to my variable.
-(void)createPlayer {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MPMoviePlayerController *temp = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:#"URLString"]];
[self performSelectorOnMainThread:#selector(passPlayer:) withObject:temp waitUntilDone:YES];
[temp release];
[pool release];
}
-(void)passPlayer:(MPMoviePlayerController*)temp {
player = [temp retain];
player.movieSourceType = MPMovieSourceTypeStreaming;
//player.view.hidden = YES;
//[self.view addSubview:player.view];
[player prepareToPlay];
}
The problem is when I print out the duration of the player I get this:
double duration = player.duration;
NSLog(#"duration: %f, duration);
console output: duration: nan
I have added the MPMoviePlayerControllerNotifications to be notified when there is a duration value available, but even then the value is still 'nan'. If I do the load on the main thread the duration has a value, but I don't want to use the main thread so my UI doesn't lock up.
Any ideas?
First of all, don't create UI objects on a non-main thread. All UI operations must be done on the main thread, or odd things will occur. Such as, perhaps, the movie never loading and NAN being returned for the duration.
Second, initWithContentURL: probably is asynchronous or there wouldn't be notifications to let you know when the movie was actually loaded. Just create the controller as you would normally, subscribe to the appropriate notifications to know when the status is updated, and then check the status immediately just in case the movie happened to be a local file or already cached so the load was able to happen synchronously.
This happens because the movie player has not been able to calculate the duration. If you subscribe to the MPMovieDurationAvailableNotification notification, you'll be notified if and when a value has been determined.
MPMovieDurationAvailableNotification
This notification is posted when the
duration of a movie object is
determined. The object of the
notification is the
MPMoviePlayerController object itself.
There is no userInfo dictionary. The
duration value is reflected in the
duration property of the movie player
controller.
Source: Apple documentation
From the official doc :
Discussion
If the duration of the movie is not known, the value in this property is 0.0. If the duration is subsequently determined, this property is updated and a MPMovieDurationAvailableNotification notification is posted.
So use NSNotificationCenter to register the MPMovieDurationAvailableNotification notification.

MPMoviePlayer dismiss when pressed

I have a MPMoviePlayer setup to play an intro movie to my application. That works just great, the only problem is that it lasts for 14 seconds, and I want to give my users a chance to skip the intro by pressing anywhere on the movie.
I have hidden the movie controls, as they are not needed.
Code:
NSString *introPath = [[NSBundle mainBundle] pathForResource:#"intro" ofType:#"mov"];
intro = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:introPath]];
[intro setMovieControlMode:MPMovieControlModeHidden];
[intro play];
Thank you!
EDIT: My initial solution won't work, because the movie is shown in a second window, layered on top of the app's main window (it's very rare to have more than one window in the view hierarchy on the iPhone). This solution, based on Apple's MoviePlayer sample code, does work:
. . .
// assuming you have prepared your movie player, as in the question
[self.intro play];
NSArray* windows = [[UIApplication sharedApplication] windows];
// There should be more than one window, because the movie plays in its own window
if ([windows count] > 1)
{
// The movie's window is the one that is active
UIWindow* moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
// Now we create an invisible control with the same size as the window
UIControl* overlay = [[[UIControl alloc] initWithFrame:moviePlayerWindow.frame]autorelease];
// We want to get notified whenever the overlay control is touched
[overlay addTarget:self action:#selector(movieWindowTouched:) forControlEvents:UIControlEventTouchDown];
// Add the overlay to the window's subviews
[moviePlayerWindow addSubview:overlay];
}
. . .
// This is the method we registered to be called when the movie window is touched
-(void)movieWindowTouched:(UIControl*)sender
{
[self.intro stop];
}
NB: You must save the reference to the movie player in an instance variable, and it's most convenient to declare a property that we can use to access it. That's why is use self.intro instead of just intro in the example. If you don't know how to declare an instance variable and a property, there is plenty of information on this site and elsewhere.
**** ORIGINAL ANSWER BELOW
(Doesn't work in this case, but in many similar scenarios, so I'll leave it as a warning and/or inspiring example.)
. . . if nothing else works I'd recommend subclassing UIWindow and making sure that your app delegate instantiates that instead of a normal UIWindow. You can intercept touches in that class and send a notification or cancel the movie directly (if you've stored a pointer to the MPMoviePlayer in an ivar on your window subclass).
#interface MyWindow : UIWindow {
}
#end
#implementation MyWindow
// All touch events get passed through this method
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// The screen has been touched, send a notification or stop the movie
return [super hitTest:point withEvent:event];
}
#end

Loading Multiple Movies in iPhone MPMoviePlayerController

-(void)initAndPlayMovie:(NSURL *)movieURL
{
// Initialize a movie player object with the specified URL
MPMoviePlayerController *mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
if (mp)
{
self.moviePlayer = mp;
[mp release];
[self.moviePlayer play];
}
}
Here, in above code, We can pass just one movie URL. Isn't it possible to pass multiple urls to it?
So, Movie Player will load second url after playing first one.
Is it possible? How can we do that?
Right now, when I try to pass other url, after finishing first one.
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[self initAndPlayMovie:secondURL];
}
The Device First change its orientation while loading and after loading Device again come back to landscape mode.
How to resolve this problem?
You might want to change the orientation by changing the statusBar orientation before you start playing videos and change it back after you are done with all.
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:YES];
You should be able to call setContentURL just as the first movie is about to close to change to another movie. Check endPlaybackTime and fire off your method to invoke setContentURL one second prior to the movie ending.

MPMoviePlayerController and overlay window

Iam trying to play a video from a remote location and trying to overlay a window over the entire screen which is more or less transparent with few images along the edges . As soon as the movie is preloaded I play the video. This happens good the first time. But if I try to replay the video it does not play the video and neither am i able to click on the Play button from the controls of the player. I guess the overlay window is over the controls. How do i get the controls over the overlay window.
//- (void)moviePlayerContentPreloadDidFinish:(NSNotification *)notification{
NSLog(#"content preloaded");
NSDictionary *userInfo = [notification userInfo];
if ([userInfo valueForKey:#"error"]) {
NSLog(#"*** An error occurred preloading the movie");
return;
}
[self.spinner stopAnimating];
// Add the overlay view to the movie, so we can catch the clicks
OverlayViewController *ovc = [[OverlayViewController alloc] initWithNibName:#"OverlayView" bundle:nil];
self.overlayController = ovc;
[ovc release];
[self.overlayController setNextResponder:self];
MPMoviePlayerController *moviePlayer = [notification object];
[moviePlayer play];
// Locate the movie player window
NSArray *windows = [[UIApplication sharedApplication] windows];
NSLog(#"window count= %d",[windows count]);
if ([windows count] < 2) {
NSLog(#"*** Window for movie player is missing");
return;
}
UIWindow *moviePlayerWindow = [windows objectAtIndex:1];
[moviePlayerWindow addSubview:self.overlayController.view];
}
The code that I use is
Am I doing something wrong. Is it possible to get the controls over the overlay or auto play it.
Two things.
First, according to your code, in the moviePlayBackDidFinish method you must first release the moviePlayer and then instantiate it again with an alloc. You must do that every time you want to play a video.
MPMoviePlayerController *moviePlayer = [notification object];
[moviePlayer release]
...
MPMoviePlayerController *newMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL: anURL];
The structure of your code works, but I think you should better have a look at the example provided by apple:
http://developer.apple.com/iphone/library/samplecode/MoviePlayer_iPhone/index.html
Second, about the overlay, you are right, the overlay gets the events but you cannot put it under the movie player controls. What you have to do is to use the events you receive in your overlay to control the player. You could use touchesBegan event to pass the initial touch event and use it with the player (play, pause, stop).

Overlay View Problem When Playing Two Different Movies in Sequence

I'm trying to play two videos using the MPMoviePlayerController class and allow the user to switch between the two videos by swiping their finger across the screen. Everything works great for the first movie. The overlay view correctly detects the swipe and starts the next movie playing.
Unfortunately things don't work so well with the second movie. I'm not sure what's happening, but the overlay view does not actually seem to be on top of the movie player and is certainly not responding to touch events. In fact, double tapping the screen while the second movie is playing zooms the movie in and out, so touches seem to be going to the MPMoviePlayerController.
I've tried a number of different approaches here, but none of them work. Here is the current version of the code:
- (void)allocateMoviePlayerForCurrentVideo
{
// Create a movie player for the first video in the playlist
NSURL *url = [videoURLs objectAtIndex:nowPlayingVideoIndex];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
moviePlayer.scalingMode = MPMovieScalingModeAspectFill;
moviePlayer.movieControlMode = MPMovieControlModeHidden;
moviePlayer.backgroundColor = [UIColor blackColor];
// Register for the movie preload complete notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadComplete:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
// Register for the playback did finish notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieDidFinishPlaying:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
}
The code which handles the MPMoviePlayerContentPreloadDidFinishNotification starts the movie playing and adds the overlay view:
- (void)moviePreloadComplete:(NSNotification *)notification
{
// Remove this object from observing the preload complete notification
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
// Start playing the current movie
[moviePlayer play];
// Add the overlay view to the movie player
UIWindow *moviePlayerWindow= [[[UIApplication sharedApplication] windows] objectAtIndex:1];
overlayView = [[GGDMovieOverlayView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
overlayView.backgroundColor = [UIColor clearColor];
[moviePlayerWindow addSubview:overlayView];
[moviePlayerWindow bringSubviewToFront:overlayView];
// Register for swipe notifications from the overlay view
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleMoviePlayerSwipeNotification:)
name:GGDMoviePlayerSwipeNotification
object:nil];
}
And finally, here's the routine which handles the swipe notification:
- (void)handleMoviePlayerSwipeNotification:(NSNotification *)notification
{
// Stop the current movie player and release it
[moviePlayer stop];
[moviePlayer release];
moviePlayer = nil;
// Remove the overlay view from its superview
[overlayView removeFromSuperview];
// Advance to the next movie, treating the array of video URLs as a circular array
if ( ++nowPlayingVideoIndex == [videoURLs count] ) {
nowPlayingVideoIndex = 0;
}
[self allocateMoviePlayerForCurrentVideo];
}
I fear there's something obvious I'm missing, but this has been driving me crazy for a while now. I very much appreciate any help!
Thanks,
Adam
I just went through this myself and I can explain the issue and a slightly better workaround.
The problem with running the next movie in sequence is that the notification that you get when the movie has finished playing occurs before the current movie player window has been removed from the view. This is an issue because (as per the Apple example) the only way to get the movie player window is to grab the top window on the stack (or the key window). But if you try to use this technique immediately you'll get the old movie player window that is fading out, not the new one that is going to start. (As an aside - frankly, the Apple example is an illustration that there really isn't a proper way to get the movie window at all.. their example is terrible and is probably a race condition to begin with unless somehow the play method blocks until the window is actually on the stack).
Anyway, rather than an arbitrary delay, what I did is to tag the player window at the time that it is first created e.g.
UIWindow *moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
moviePlayerWindow.tag = MY_MOVIE_WINDOW_TAG; // this is just an int
and then in the moviePlaybackDidFinish method I call a waitForMovieWindowToExit method that looks for that tagged window. If it's still there then I use a timer to check again in 0.1 seconds. When it finally disappears I play the next sequence.
So, it's still a mess and still using a timer, but at least you are guaranteed to play the next movie within 0.1 second (or whatever) of the next possible time and it won't break if the system is a little slow, etc.
If anyone wants more code I can post it.
Fixed by using a NSTimer to add the overlay view after a delay to make sure the movie has started playing.
Thanks,
Adam