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).
Related
I'm trying to implement a notification in to my movie player, so that once the movie is done playing, it will exit fullscreen. The code is have so far i posted below. The IBAction is hooked up to a button. Also on a side note, i have been trying to figure out how to get the "play symbol" on top of my movie view, so that the user just have to press that and the video will start. Anybody know how to implement that?
- (void)viewDidLoad
{
//Video player
NSString *url = [[NSBundle mainBundle] pathForResource:self.navigationItem.title ofType:#"mov"];
_player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath: url]];
_player.view.frame = CGRectMake(350, 200, 400, 400);
[self.view addSubview:_player.view];
}
- (IBAction)playMovie
{
[_player play];
}
You can detect when your movie finishes playing by registering for an MPMoviePlayerPlaybackDidFinishNotification notification in your viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:_player];
And then perform whatever action you want to do in the callback method you specified when registering:
- (void) playerPlaybackDidFinish:(NSNotification*)notification
{
// movie finished playing
_player.fullscreen = NO;
}
OK, there are very few options to emulate the splash video in iOS. All we can do is wait till application is fully loaded and then create Media Player and load video in it.
I implemented it with following code:
-(void) moviePlayBackDidFinish:(NSNotification*)notification
{
NSLog(#"Intro video stopped");
[mMoviePlayer release];
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSURL* mMovieURL;
NSBundle *bundle = [NSBundle mainBundle];
if(bundle != nil)
{
NSString *moviePath = [bundle pathForResource:#"intro" ofType:#"mp4"];
if (moviePath)
{
mMovieURL = [NSURL fileURLWithPath:moviePath];
[mMovieURL retain];
}
}
mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:mMovieURL];
[mMovieURL release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:mMoviePlayer];
mMoviePlayer.controlStyle = MPMovieControlStyleNone;
[mMoviePlayer.backgroundView addSubview:[[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Splash/background.png"]] autorelease]];
mMoviePlayer.scalingMode = MPMovieScalingModeFill;
[window addSubview:mMoviePlayer.view];
[mMoviePlayer setFullscreen:YES animated:NO];
[window makeKeyAndVisible];
[mMoviePlayer play];
<... other stuff ...>
}
My video is only 1 MB. But this code do something different then I'd like to see:
First of all user can see a static splash screen for a few seconds;
Then a black screen appears for 1 or 2 seconds. I think this is happening because the media player is loaded.
Video start playing.
Main interface loads.
As you understand I don't like the pause with black screen - it looks ugly.
As far as I can see in my Console log the problem is that mediaplayer is waiting till the main view controller is fully loaded.
Few words about main view: i'm writing an application for iPad and the main view consists of several subviews with multiple images. Every image and every subview in main view loads some data from Internet Web service via ASIHTTPRequest lib.
I think that Media Player is waiting for all initial connections to finish and only then it's starting the video...
How can I force the video to play before main view is loaded? Or maybe I can delay the loading of main XIB?
You cannot get rid of the static splash image. While it is shown, the OS is loading the application and instantiating stuff until it is ready to call your UIApplicationDelegate. So all you can do is either use no splash (black screen for a few seconds) or make your movie start exactly with the shown splash screen so it looks like the static image would suddenly animate.
To get rid of the black screen while the movie loads, you can try to make the player transparent and have an UIImageView behind the player that shows the splash image. The behavior would be this:
Splash screen is shown (static image).
Application is loaded. You see the UIImageView, also showing the splash screen. On top of it is the transparent movie player.
Movie player finally has loaded the move and starts playing it.
At least in theory, this should cause the effect that the static image suddenly starts animating.
But if you don't use a splash screen at all (a lot of games do that), then it doesn't matter that the movie player is showing a black screen at first, you wouldn't notice.
Regarding showing the splash screen in an UIImageView: unfortunately, you have to test the interface rotation and load the image manually, there's no way to query which splash screen was shown. If you only support one interface orientation (again, a lot of games do this) you don't have this problem, of course.
There is a better solution now, assuming you are using UIViewControllers.
Instead of using MPMoviePlayerController, use MPMoviePlayerViewController. Here is some sample code, adapted from the question:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSURL* mMovieURL;
NSBundle *bundle = [NSBundle mainBundle];
if(bundle != nil)
{
NSString *moviePath = [bundle pathForResource:#"intro" ofType:#"mp4"];
if (moviePath)
{
mMovieURL = [NSURL fileURLWithPath:moviePath];
[mMovieURL retain];
}
}
mMoviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:mMovieURL];
[mMovieURL release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish)
name:MPMoviePlayerPlaybackDidFinishNotification
object:mMoviePlayer.moviePlayer];
mMoviePlayer.moviePlayer.controlStyle = MPMovieControlStyleNone;
[mMoviePlayer.moviePlayer.backgroundView addSubview:[[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"SplashCopy.png"]] autorelease]];
mMoviePlayer.moviePlayer.scalingMode = MPMovieScalingModeFill;
[window.rootViewController.view addSubview:mMoviePlayer.moviePlayer.view];
[mMoviePlayer.moviePlayer setFullscreen:YES animated:NO];
[mMoviePlayer.moviePlayer play];
}
Emulating splash video in iOS application
this code for swift4.0
var mMovieURL: URL?
let bundle = Bundle.main
if bundle != nil {
let moviePath: String? = bundle.path(forResource: "intro", ofType: "mp4")
if moviePath != nil {
mMovieURL = URL(fileURLWithPath: moviePath ?? "")
}
}
mMoviePlayer = MPMoviePlayerController(contentURL: mMovieURL!)
NotificationCenter.default.addObserver(self, selector: #selector(self.moviePlayBackDidFinish), name: .MPMoviePlayerPlaybackDidFinish, object: mMoviePlayer)
mMoviePlayer.controlStyle = .none
mMoviePlayer.backgroundView.addSubview(UIImageView(image: UIImage(named: "Splash.png")))
mMoviePlayer.scalingMode = .fill
window?.addSubview(mMoviePlayer.view)
// window?.rootViewController?.view.addSubview(mMoviePlayer.view)
mMoviePlayer.setFullscreen(true, animated: false)
window?.makeKeyAndVisible()
mMoviePlayer.play()
return true
}
#objc func moviePlayBackDidFinish(_ notification: Notification) {
mMoviePlayer.view.removeFromSuperview()
}
-(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.
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
I would like to write an app that downloads (or streams) a video (encoded as required) in a view. I dont want to use the MPVideoPlayer from the SDK as it opens the video in full screen. I would like to place another UIView (transparent) over the video so that my users can annotate over the video.
Anyone have any idea or can point me to some code that will play video in a UIView?
If you want to do this you will need to include your own (software) video decoder, which will not have access to the hardware acceleration on the system. Even if you can get it working with acceptable performance it will be a huge battery drain.
If you wish to play a video in portrait mode, I have solution for that.
If you think that -MPMovie Player can run under a view, according to me it's not possible.
MP Movie player will work as Apple has designed.
So, MP Movie player will always / almost run in full screen.
Solution for portrait mode.
#interface MPMoviePlayerController (extend)
-(void)setOrientation:(int)orientation animated:(BOOL)value;
#end
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieUR];
[moviePlayer setOrientation:UIDeviceOrientationPortrait animated:NO];
if (moviePlayer)
{
[self.moviePlayer play];
}
Hope that will help to you.
See, My question is very similar like yours.
playing video in custom size screen - view in iphone
Try this:
UIViewController *v = [[UIViewController alloc] init];
v.view.backgroundColor = [UIColor orangeColor];
NSString *path = [[NSBundle mainBundle] pathForResource:#"demo" ofType:#"mp4"];
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
{
NSLog(#"cannot find %# in bundle or doctuments", path);
}
NSURL *url = [NSURL fileURLWithPath:path];
MoviePlayerViewController *mpvc = [[MoviePlayerViewController alloc] initWithContentURL:url];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:mpvc.moviePlayer];
mpvc.moviePlayer.fullscreen = NO;
[mpvc.moviePlayer setControlStyle:MPMovieControlStyleNone];
mpvc.moviePlayer.view.frame = CGRectMake(10, 100, 300, 300);
[v.view addSubview:mpvc.moviePlayer.view];
[mpvc.moviePlayer play];
[self presentModalViewController:v animated:YES];
[v release];
Maybe you should check the MediaPlayer's private headers, and simply add the video's view as your view's subview.