I went through the example from apple "MoviePlayer on iPhone"
Im trying to overlay on top of the mpmovieplayercontroller,
it works perfectly with video clip that is in bundle,
but it wont work if i stream the video from the url.
the overlay view will just get hide behind the player.
is there a way to bring the overlay view up to front?
MPMoviePlayerController creates its own window and sets that as the key window - you probably know this already from the MoviePlayer sample app.
I don't know why, but there's a delay when the player uses a stream - so the keyWindow you get right after you initialize the player is likely not the player's window, since that seems to get added later.
You can "cheat" and use a timer to get the player window a few seconds later, and add your overlay:
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(addMyOverlay:) userInfo:nil repeats:FALSE]
Or you can listen for the UIWindowDidBecomeKeyNotification event, and do the same:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyWindowChanged:) name:UIWindowDidBecomeKeyNotification object:nil];
Neither option is great (I'd love to know a cleaner way to do this), but it gets the job done.
You can overlay your view when you receive "MPMoviePlayerContentPreloadDidFinishNotification" notification.
Register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
Add overlay view when receiving the notification:
// Notification called when the movie finished preloading.
- (void) moviePreloadDidFinish:(NSNotification*)notification
{
NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count] > 1)
{
// Locate the movie player window
UIWindow *moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
if ([moviePlayerWindow viewWithTag:0x3939] == nil) {
self.videoOverlayView.tag = 0x3939;
[moviePlayerWindow addSubview:self.videoOverlayView];
}
[moviePlayerWindow bringSubviewToFront:self.videoOverlayView];
}
}
A very simple solution:
appDelegate.window.backgroundColor = [UIColor clearColor];
appDelegate.window.windowLevel = 2;
This will keep your app UI on top of the video window.
my post
Previous answer was based on timer. & fixed 5 seconds.
When Movie player begins, a new window is added to application.
Use a timer to check weather a new window is added to your application or not.
When a window ( movie player window ) is added. set notifications.
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
// Register to receive a notification when the movie scaling mode has changed.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieScalingModeDidChange:)
name:MPMoviePlayerScalingModeDidChangeNotification
object:nil];
videoListController.xmlClassVideoList=t;
// here ttttt is a timer declared in .h file
tttttt=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(startMy) userInfo:nil repeats:YES];
}
-(void)startMy{
NSArray *windows = [[UIApplication sharedApplication] windows];
NSLog(#"%i",[windows count]);
// depends on your application window
// it may be 1/2/3
if ([windows count] > 3) {
// Locate the movie player window
[tttttt invalidate];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyWindowChanged:) name:UIWindowDidBecomeKeyNotification object:nil];
}
}
Related
I hvae addd one webview having youtube link. When user play viedo it defaults open iOS movie player. I want to track notification of that movie player when it exits full screen or playing stopped. I have tried all notification generated by MPMoviewPlayerController . None of them are being fired. It fiers only when we instatinate MPMoviewPlayerViewCotntroller object and present MPMoviewPlayer from that.
That's becuase Youtube videos inside UIWebView are not MPMoviewPlayerViewCotntroller.
On iOS7:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerWillEnterFullscreen:)
name:#"UIMoviePlayerControllerDidEnterFullscreenNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerWillExitFullscreen:)
name:#"UIMoviePlayerControllerWillExitFullscreenNotification" object:nil];
On iOS8 it's a bit of a problem because these events are gone, and you need to add observer like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ios8EnterFullscreen:)
name:UIWindowDidBecomeVisibleNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ios8ExitFullscreen:)
name:UIWindowDidBecomeHiddenNotification object:nil];
And check that when it fires it is indeed a movie player (because it fires also on UIAlertView and stuff):
- (void)ios8EnterFullscreen:(NSNotification *)notification
{
if ([notification.object isMemberOfClass:[UIWindow class]])
{
//do your thing...
}
}
I want to play play video on current viewcontroller with full screen.
so i am using MPMoviePlayerViewController to play full screen video but problem is that it does not play smooth.
it lag little bit while playing video.
Here is My code which i am using to play full screen video which is store in my documents directory of phone/ipad
-(void)PlayVideo:(NSString *)videoFilePath
{
NSURL *videoURL = [NSURL fileURLWithPath:videoFilePath];
NSLog(#"videoURL: %#", videoURL);
MPMoviePlayerViewController *playerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver:playerVC
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
// Register this class as an observer instead
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
// Set the modal transition style of your choice
playerVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
// Present the movie player view controller
[self presentViewController:playerVC animated:NO completion:nil];
// Start playback
[playerVC.moviePlayer prepareToPlay];
[playerVC.moviePlayer play];
}
- (void)movieFinishedCallback:(NSNotification*)aNotification
{
// Obtain the reason why the movie playback finished
NSNumber *finishReason = [[aNotification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
// Dismiss the view controller ONLY when the reason is not "playback ended"
if ([finishReason intValue] != MPMovieFinishReasonPlaybackEnded)
{
MPMoviePlayerController *moviePlayer = [aNotification object];
// Remove this class from the observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
// Dismiss the view controller
[self dismissViewControllerAnimated:YES completion:Nil];
}
}
Didn't find any issue with the code except in
[[NSNotificationCenter defaultCenter] removeObserver:playerVC
name:MPMoviePlayerPlaybackDidFinishNotification
object:playerVC.moviePlayer];
removeObserver should be self and not playerVC.
But ya, that will not cause any problem like you have mentioned.
I would suggest you try profiling your app, which might direct you to the code causing the issue. Probably there might be something that you are doing in the background when the video is playing which is causing it to lag.
I write custom player from AVPlayer for video playback. According to Apple docs set the video layer:
self.player = [IPLPlayer new];
self.player.playerLayer = (AVPlayerLayer *)self.playerView.layer;
Where self.playerView is usual class from those docs:
#implementation PlayerView
+ (Class) layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer *)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *) player {
[(AVPlayerLayer *) [self layer] setPlayer:player];
}
The problem is:
When close app (Home button), or block screen, the video playback is stopped, and when resume ONLY audio playback resumed, the image on screen is still those was before block screen - it's fully static and note change frames.
How to resume VIDEO playing after screen is blocked?
Seems I must to register notifications, and after app become active resume video layer:
-(void)registerNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(willEnterBackground)
name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didEnterForeground)
name:UIApplicationDidBecomeActiveNotification object:nil];
}
-(void)unregisterNotification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)willEnterBackground
{
NSLog(#"willEnterBackground");
[self.playerView willEnterBackground];
}
-(void)didEnterForeground
{
NSLog(#"didEnterForeground");
[self.playerView didEnterForeground];
}
And one solution that binds all this information together.
Maybe player status should be handled differently, but I like the recursive way.
Note: If you do not need the exact seek time, you can use [_player seekToTime:<#(CMTime)#> completionHandler:<#^(BOOL finished)completionHandler#>] It's faster but it seeks to the nearest key frame.
- (void)viewDidLoad
{
[super viewDidLoad];
....
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appEnteredBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
....
-(void) appEnteredForeground {
AVPlayerLayer *player = (AVPlayerLayer *)[playerView layer];
[player setPlayer:NULL];
[player setPlayer:_player];
[self playAt:currentTime];
}
-(void) appEnteredBackground {
[_player pause];
currentTime = [_player currentTime];
}
-(void)playAt: (CMTime)time {
if(_player.status == AVPlayerStatusReadyToPlay && _player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
[_player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
[_player play];
}];
} else {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self playAt:time];
});
}
}
This works for me on Swift 3
Add somewhere while setting up the view:
NotificationCenter.default.addObserver(self,
selector: #selector(appWillEnterForegroundNotification),
name: .UIApplicationWillEnterForeground, object: nil)
Grab your player and force it to play:
func appWillEnterForegroundNotification() {
myPlayer.play()
}
Don't forget to remove the observers when you don't need them:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
Use the UIApplicationWillEnterForegroundNotification as well. That way you know your app will be active and visible to the user:
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(appEnteredForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
The trick is to detach all video layers from their players when the app did enter background and reattaching them when the app did become active again.
So in your -applicationDidEnterBackground: you got to trigger a mechanism that results in
avPlayerLayer.player = nil;
and in your -applicationDidBecomeActive: you reattach the player like
avPlayerLayer.player = self.avPlayer;
Also have a look at this Tech Note (QA1668) from Apple.
After some research I've found, that the same bag is in iOS player (WebBrowser, MPMoviePlayerController). May be because distribution type of content is Progressive Download.
So solution is:
Use below notifications and save current time.
After app is resumed, recreate AVPlayer and start playing from saved
time.
In all other cases image is blocked, or view become black.
I am playing a video in a view controller. When the user hits the hardware home button and the video is currently playing the app crashes with a EXC_BAD_ACCESS in the simulator.
I read that I should use the applicationWillResignActive message to stop the video from playing which should solve the crashing. So I am trying to register for this notifcation with the notification center, but my selector never gets called. What am I doing wrong?
The following code is in my media player view controller:
- (void) playMedia {
NSURL *mediaUrl = [NSURL fileURLWithPath:tmpFilePath isDirectory:FALSE];
player = [[MPMoviePlayerViewController alloc] initWithContentURL:mediaUrl];
player.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
player.view.frame = self.view.frame;
[self.view addSubview:player.view];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
[player.moviePlayer play];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
// never gets called!
NSLog(#"resign active");
[player.moviePlayer stop];
}
Note that if you have the UIApplicationExitsOnSuspend key set to true in your app's Info.plist, the applicationWillResignActive method is not called when the user hits the home button.
Not sure why that one isnt working for you, but im using
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopAction:) name:UIApplicationDidEnterBackgroundNotification object:nil];
with success in an Audio Player/Recorder.
possibly try implementing
- (void)applicationWillResignActive:(NSNotification *)notification {
}
in the app delegate and see if it calls.
I'm having lots of trouble displaying a fullscreen movie in my iPad app and then allowing the user to dismiss it with either the Done button or the "un-fullscreen" button on the player controls.
Initially I was using MPMoviePlayerViewController for the movie presentation, but I wasn't receiving the enter/exit fullscreen notifications from its MPMoviePlayerController object, so I switched to doing it myself.
I can make the movie appear fullscreen (although the transition is janky), but when either the "Done" or "un-fullscreen" buttons are pressed, no action is taken by the player. I've posted my code below:
- (void)startPlayingMovieWithURLString:(NSString *)movieURLString {
// I get all of these callbacks **EXCEPT** the "willExitFullScreen:" callback.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullScreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullScreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishPlayback:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[self.moviePlayerController setContentURL:someExistingURL];
// "self" is a UIViewController subclass, and is presented as a "fullscreen" modal view controller from its parent
// I'm setting the movie player's view's frame to take up the full rectangle of my view controller, but really I want the movie to be completely removed when the user presses "done" (that is, removed from the view hierarchy). Not sure when/where to do this.
self.moviePlayerController.view.frame = self.view.frame;
[self.view addSubview:self.moviePlayerController.view];
[self.moviePlayerController setFullscreen:YES animated:YES];
}
And here is the code for my didFinish callback
- (void)didFinishPlayback:(NSNotification *)notification {
// This ends up recursively telling the player that playback ended, thus calling this method, thus…well you get the picture.
// What I'm trying to do here is just make the player go away and show my old UI again.
[self.moviePlayerController setFullscreen:NO animated:YES];
}
So obviously I am doing something wrong but I've been up and down the documentation and I can't figure out how to make the movie just go away. I figured it would be more intuitive than this. What am I doing wrong?
Here are how the events -> notifications work:
User presses 'Done' button
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
User presses 'Leave fullscreen' button on transport
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
Note that playback does not stop
Movie reaches end
MPMoviePlayerPlaybackDidFinishNotification with the MPMoviePlayerPlaybackDidFinishReasonUserInfoKey set to MPMovieFinishReasonPlaybackEnded
If you call setFullscreen:NO animated:YES on your MoviePlayerController instance from this notification, you'll then get the WillExit and DidExit notifications.
Note that you don't get the PlaybackDidFinish notification when the user presses the Done or Leave Fullscreen buttons.
So, typically, if you want to get rid of the MoviePlayer's view, you need to put [self.moviePlayerController.view removeFromSuperview] in the DidExitFullscreen notification handler. WillExitFullscreen is too soon.
Here's my code:
- (void)willEnterFullscreen:(NSNotification*)notification {
NSLog(#"willEnterFullscreen");
}
- (void)enteredFullscreen:(NSNotification*)notification {
NSLog(#"enteredFullscreen");
}
- (void)willExitFullscreen:(NSNotification*)notification {
NSLog(#"willExitFullscreen");
}
- (void)exitedFullscreen:(NSNotification*)notification {
NSLog(#"exitedFullscreen");
[self.movieController.view removeFromSuperview];
self.movieController = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)playbackFinished:(NSNotification*)notification {
NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch ([reason intValue]) {
case MPMovieFinishReasonPlaybackEnded:
NSLog(#"playbackFinished. Reason: Playback Ended");
break;
case MPMovieFinishReasonPlaybackError:
NSLog(#"playbackFinished. Reason: Playback Error");
break;
case MPMovieFinishReasonUserExited:
NSLog(#"playbackFinished. Reason: User Exited");
break;
default:
break;
}
[self.movieController setFullscreen:NO animated:YES];
}
- (void)showMovie {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteredFullscreen:) name:MPMoviePlayerDidEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exitedFullscreen:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
NSURL* movieURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"tron" ofType:#"mov"]];
self.movieController = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
self.movieController.view.frame = self.view.frame;
[self.view addSubview:movieController.view];
[self.movieController setFullscreen:YES animated:YES];
[self.movieController play];
}
Yes. That's great. There are really notifications mentioned above...
However, there are no MPMoviePlayerPlaybackWillFinishNotification somewhy!!!
That's really a problem.
When you call the movie player as modal (no matter which of the following methods used presentViewController/presentModalViewController/presentVideoController), if you defined .fullScreen = YES, it's not expected to call MPMoviePlayerWillExitFullscreenNotification notification at all (obviously, because it's not cosidering we enter/exit from full screen, but only present/dismiss the controller).
But there are really no any notifications that the video is about to finish and close. That's needed (besides any other situations possible) to catch the moment when the transition of dismissing is started. (The transition, of course, starts before the MPMoviePlayerPlaybackDidFinishNotification called). And, at the same time, application:supportedInterfaceOrientationsForWindow: for previously shown controller is called before the notification, and there is no way to say the AppDelegate that our current controller must be shown already in another orientation.
So, since my video is fullscreen and also without any controls shown (this is kind of an intro, so I just until it finishes) my solution was just to have a timer which checks every short tick (0.1 seconds) what is the video current position... and it it's close to the end, then this is the moment for my own notification.