-(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.
Related
I am using Avplayer to show video clips and when i go back (app in background) video stop. How can i keep playing the video?
I have search about background task & background thread ,IOS only support music in background (Not video)
http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
here is some discussion about play video in background
1) https://discussions.apple.com/thread/2799090?start=0&tstart=0
2) http://www.cocoawithlove.com/2011/04/background-audio-through-ios-movie.html
But there are many apps in AppStore, that play video in Background like
Swift Player : https://itunes.apple.com/us/app/swift-player-speed-up-video/id545216639?mt=8&ign-mpt=uo%3D2
SpeedUpTV : https://itunes.apple.com/ua/app/speeduptv/id386986953?mt=8
This method supports all the possibilities:
Screen locked by the user;
List item
Home button pressed;
As long as you have an instance of AVPlayer running iOS prevents auto lock of the device.
First you need to configure the application to support audio background from the Info.plist file adding in the UIBackgroundModes array the audio element.
Then put in your AppDelegate.m into
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions:
these methods
[[AVAudioSession sharedInstance] setDelegate: self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
and #import < AVFoundation/AVFoundation.h >
Then in your view controller that controls AVPlayer
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
and
- (void)viewWillDisappear:(BOOL)animated
{
[mPlayer pause];
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
then respond to the
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
if([mPlayer rate] == 0){
[mPlayer play];
} else {
[mPlayer pause];
}
break;
case UIEventSubtypeRemoteControlPlay:
[mPlayer play];
break;
case UIEventSubtypeRemoteControlPause:
[mPlayer pause];
break;
default:
break;
}
}
Another trick is needed to resume the reproduction if the user presses the home button (in which case the reproduction is suspended with a fade out).
When you control the reproduction of the video (I have play methods) set
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
and the corresponding method to be invoked that will launch a timer and resume the reproduction.
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
[mPlayer performSelector:#selector(play) withObject:nil afterDelay:0.01];
}
Its works for me to play video in Backgorund.
Thanks to all.
If you try to change the background mode:
Sorry, App store wont approve it.MPMoviePlayerViewController playback video after going to background for youtube
In my research, someone would take the sound track out to play in te background when it goes into background as the video would be pause and get the playbacktime for resume playing when go into foreground
It is not possible to play background music/video using Avplayer. But it is possible using
MPMoviePlayerViewController. I have done this in one of my app using this player & this app
is run successfully to appstore.
Try with this snippet, I've already integrated this with my app & it's being useful for me..hope this will work for you!!
Follow the steps given below:
Add UIBackgroundModes in the APPNAME-Info.plist, with the selection
App plays audio
Then add the AudioToolBox framework to the folder frameworks.
In the APPNAMEAppDelegate.h add:
-- #import < AVFoundation/AVFoundation.h>
-- #import < AudioToolbox/AudioToolbox.h>
In the APPNAMEAppDelegate.m add the following:
// Set AudioSession
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
/* Pick any one of them */
// 1. Overriding the output audio route
//UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
//AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
sizeof(audioRouteOverride), &audioRouteOverride);
========================================================================
// 2. Changing the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
into the
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
but before the two lines:
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
Enjoy Programming!!
Swift version for the accepted answer.
In the delegate:
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
In the view controller that controls AVPlayer
override func viewDidAppear(animated: Bool) {
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
self.becomeFirstResponder()
}
override func viewWillDisappear(animated: Bool) {
mPlayer.pause()
UIApplication.sharedApplication().endReceivingRemoteControlEvents()
self.resignFirstResponder()
}
Don't forget to "import AVFoundation"
In addition to fattomhk's response, here's what you can do to achieve video forwarding to the time it should be after your application comes in foreground:
Get currentPlaybackTime of playing video when go to background and store it in lastPlayBackTime
Store the time when application goes to background (in userDefault probably)
Again get the time when application comes in foreground
Calculate the duration between background and foreground time
Set current playback time of video to lastPlayBackTime + duration
If you are Playing video using WebView you can handle using javascript to play video on background.
strVideoHTML = #"<html><head><style>body.........</style></head> <body><div id=\"overlay\"><div id=\"youtubelogo1\"></div></div><div id=\"player\"></div> <script> var tag = document.createElement('script'); tag.src = \"http://www.youtube.com/player_api\"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player; events: { 'onReady': onPlayerReady, } }); } function onPlayerReady(event) { event.target.playVideo(); } function changeBG(url){ document.getElementById('overlay').style.backgroundImage=url;} function manualPlay() { player.playVideo(); } function manualPause() { player.pauseVideo(); } </script></body> </html>";
NSString *html = [NSString stringWithFormat:strVideoHTML,url, width, height,videoid;
webvideoView.delegate = self;
[webvideoView loadHTMLString:html baseURL:[NSURL URLWithString:#"http://www.your-url.com"]];
On view diappear call
strscript = #"manualPlay();
[webvideoView stringByEvaluatingJavaScriptFromString:strscript];
I'd like to add something that for some reason ended up being the culprit for me. I had used AVPlayer and background play for a long time without problems, but this one time I just couldn't get it to work.
I found out that when you go background, the rate property of the AVPlayer sometimes seems to dip to 0.0 (i.e. paused), and for that reason we simply need to KVO check the rate property at all times, or at least when we go to background. If the rate dips below 0.0 and we can assume that the user wants to play (i.e. the user did not deliberately tap pause in remote controls, the movie ended, etc) we need to call .play() on the AVPlayer again.
AFAIK there is no other toggle on the AVPlayer to keep it from pausing itself when app goes to background.
HI,
I am developing an app which contains 3 tab view control. One each for audio, video and images.
When i select the video tab, I have all the items displayed in the table view format so that on selection of any cell, the video is shown.
Example.
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:url]] ;
[moviePlayer play];
}
The problem with this is though I am able to play the video I am unable to see the video and can hear only the audio.
In addition, if I were to close the app while the video is playing, and start it the app, the application crashes.
Can you please let me know how should I go ahead ?
After spending some more time, I have found out that for version above 3.2 one has to use MPMoviePlayerViewController.
The use of the same resolved the issue.
if ([[[UIDevice currentDevice] systemVersion ] doubleValue] >= 3.2) {
MPMoviePlayerViewController *mediaPlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:urlAddress]];
if (mediaPlayer) {
[self presentMoviePlayerViewControllerAnimated:mediaPlayer];
mediaPlayer.moviePlayer.movieSourceType = MPMovieSourceTypeFile ;
[mediaPlayer.moviePlayer play];
[mediaPlayer release];
}
}
Check out this technote from Apple:
http://developer.apple.com/library/ios/#qa/qa2010/qa1240.html
To summarize, you need to add the movie player controller's view as a subview of your app's view.
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
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).
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