here is my situation :
I call a local movie with an URL. the function is in a custom viewController
in .h :
MPMoviePlayerViewController* modalVideoController
in .m
-(void)startVideoAd:(NSNotification*)notification
{
NSURL* url = (NSURL*)[notification object];
// no problem with url ... already check :)
modalVideoController = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
[modalVideoController shouldAutorotateToInterfaceOrientation:YES];
[self presentMoviePlayerViewControllerAnimated:modalVideoController];
[modalVideoController release];
}
Problem : if the user hit the enter/exit fullscreen button (the double arrow button at right of the fastfoward button in the video button panel), the modalviewController normaly disappear but the video still playing , no images just sounds.
is there a way to kill the video after the button is pressed ?
Answer:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// avoid exitFullscreen button problem on FullScreen mode
if(modalVideoController != nil)
{
[modalVideoController.moviePlayer stop];
}
}
this way stop correctly the movie. Last details: modalVideoController became global.
Related
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()
}
I am trying to get an embedded video playback properly implemented within a tab-bar navigation. In my specific case, the video shall be displayed in a non-fullscreen manner on a UIView that is hosted by a UIViewController, managed by a UITabBarController.
For simplifying the example, lets say I have two tabs within my tabbar. First one shows some random stuff, second one shows the viewcontroller that hosts the embedded video.
Once the user selects the second tab, the video is loading and playing properly.
For initializing the player, I am using the following code from within my UIView derived class, triggered by the initializer (initWithFrame):
- (void)initPlayback
{
self.movieViewController = [[MPMoviePlayerViewController alloc] init];
movieViewController_.wantsFullScreenLayout = NO;
movieViewController_.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
[self addSubview:self.movieViewController.view];
}
For starting the playback, I am using the following code, triggered by the viewWillAppear method of my UIViewController derived class:
- (void)playVideo
{
[movieViewController_.moviePlayer setContentURL:fileURL_];
}
If then, the user selects the first tab (while the video is still playing), I am making sure that the video is stopped as it would continue playing if that was not done:
- (void)stopVideo
{
[movieViewController_.moviePlayer stop];
}
Once the user selects the second tab again, the view stays blank, nothing is loaded or played even though the playVideo-method is invoked.
What am I missing, why is the video playback failing when reselecting the second tab?
==========new attempt=============
This time I stopped relying on shouldAutoplay (as suggested) but that did not make a difference.
Adapted and added code for this;
- (void)MPMoviePlayerLoadStateDidChange:(NSNotification *)notification
{
if (movieViewController_.moviePlayer.loadState == MPMovieLoadStatePlayable &&
movieViewController_.moviePlayer.playbackState != MPMoviePlaybackStatePlaying)
{
[movieViewController_.moviePlayer play];
}
}
- (void)deregisterFromNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
- (void)registerForNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
- (void)initPlayback
{
NSLog(#"playback init...");
self.movieViewController = [[MPMoviePlayerViewController alloc] init];
movieViewController_.wantsFullScreenLayout = NO;
movieViewController_.moviePlayer.shouldAutoplay = NO;
movieViewController_.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
movieViewController_.moviePlayer.currentPlaybackTime = 0.0f;
[self addSubview:movieViewController_.view];
}
- (void)playVideo
{
NSLog(#"playback starting...");
[self registerForNotifications];
[movieViewController_.moviePlayer setContentURL:fileURL_];
}
- (void)stopVideo
{
NSLog(#"playback stopping...");
[movieViewController_.moviePlayer stop];
[self deregisterFromNotifications];
}
In playVideo I think it should be
[movieViewController_.moviePlayer setContentURL:fileURL_];
[movieViewController_.moviePlayer play];
I assume that it works the first time because autoplay defaults to YES
MPMoviePlayerViewController is a subclass of UIViewController. If the second tab is dedicated to displaying video why not just use an instance of it as the root view controller for the second tab?
Adding [movieViewController_.moviePlayer prepareToPlay] to my playVideo method does the trick (when working with remote streams).
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
-(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.
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).