I'm currently running on iOS 4.3.5 and trying to get my MPMoviePlayerViewController to continue playing after entering background.
I implemented everything as it is described on
http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
and
http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/RemoteControl/RemoteControl.html
I have also set the UIBackgroundMode to audio.
My custom MPMoviePlayerViewController class is called like this from a TabBarApplication:
NSURL *streamUrl = [NSURL URLWithString:STREAM_URL];
self.playerViewController = [[CustomMoviePlayerViewController alloc] initWithContentURL:streamUrl];
// Register for the playback finished notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMovieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.playerViewController.moviePlayer];
// Present
[self presentMoviePlayerViewControllerAnimated:self.playerViewController];
// Play the movie!
self.playerViewController.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[self.playerViewController.moviePlayer prepareToPlay];
[self.playerViewController.moviePlayer play];
Inside my CustomMovePlayerController looks like the following:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
-(void)viewWillAppear:(BOOL)animated {
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
-(void)viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)remoteControlReceivedWithEvent:(UIEvent *)event {
[super remoteControlReceivedWithEvent:event];
NSLog(#"remoteControlReceived");
NSLog(#"%d", [[AVAudioSession sharedInstance] isActive]);
if (event.type == UIEventTypeRemoteControl) {
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
[self.moviePlayer play];
break;
case UIEventSubtypeRemoteControlPause:
[self.moviePlayer pause];
break;
default:
break;
}
}
}
The main problem with my MPMoviePlayerViewController is, that it doesn't respond to the remoteControlReceivedWithEvent message, why is that? Am I subclassing the wrong thing? Does my Tabbar based app prevent me from doing that?
Last but not Least - applicationDidFinishLaunchingWithOptions contains following:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
I just can't figure out what's missing... all help is greatly appreciated!
I think I can answer both questions :)
The background issue:
What file format are you trying to play? My app has been using a MPMoviePlayerViewController for background audio for a while, but recently had reports that it wasn't playing in the background anymore.
Turns out it was AAC files; they're podcasts with chapters and cover art per chapter... iOS 5 added improved support for them, but it must make the MPMoviePlayerViewController think it's playing video. Background video was turned off around iOS 4.3, from what I can find.
Try it with a plain MP3 file, that still works for me. I'm about to log the AAC problem as a bug with Apple.
The remote control issue:
Both the lock screen and notification bar remote control buttons send the UIEventSubtypeRemoteControlTogglePlayPause event, not play and pause separately. So I handle events like this:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
if (controller.playbackState == MPMusicPlaybackStatePlaying) {
[controller pause];
} else {
[controller play];
}
break;
//etc
}
Related
I have MPMoviePlayerController that should play video's audio in background and should be controlled by the multitasking play/pause controls.
After updating .plist file with Required background modes and calling the following:
- (void)startBackgroundStreaming
{
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
NSError *activationError = nil;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&activationError];
[audioSession setActive:YES error:&activationError];
}
The app icon appears in the multitasking play/pause bar, but these buttons don't respond.
Thanks!
The missing piece of the puzzle is handling the remote control events you are receiving. You do this by implementing the -(void)remoteControlReceivedWithEvent:(UIEvent *)event method in your application delegate. In its simplest form it would look like:
-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
if (event.type == UIEventTypeRemoteControl){
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
// Toggle play pause
break;
default:
break;
}
}
}
However this method is called on the application delegate, but you can always post a notification with the event as the object so that the view controller that owns the movie player controller can get the event, like so:
-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
[[NSNotificationCenter defaultCenter] postNotificationName:#"RemoteControlEventReceived" object:event];
}
Then grab the event object in the listener method you assign to the notification.
-(void)remoteControlEventNotification:(NSNotification *)note{
UIEvent *event = note.object;
if (event.type == UIEventTypeRemoteControl){
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
if (_moviePlayerController.playbackState == MPMoviePlaybackStatePlaying){
[_moviePlayerController pause];
} else {
[_moviePlayerController play];
}
break;
// You get the idea.
default:
break;
}
}
}
My problem is - when audio is playing in background in my iPad then "my app icon" is not coming. I am using iPodMusicPlayer. For playing audio in background I have write these code..
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
// The iPod controls will send these events when the app is in the background
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
[self performSelector:#selector(playPause:)];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
//[self previousSong:nil];
//[self performSelector:#selector(previousSong:)];
break;
case UIEventSubtypeRemoteControlNextTrack:
//[self performSelector:#selector(nextSong:)];
break;
default:
break;
}
}
}
and info.plist I have also set "required background mode"
You will also have to add UIBackgroundModes to your Info.plist and set its value to audio.
Please check the iOS App Programming Guide, specifically the App States and Multitasking section for detailed info on how to execute tasks in background.
[UPDATE]
Also add these two lines to application:didFinishLaunchingWithOptions: in your AppDelegate.m:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
And this code when you start playing your audio:
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
[_player play];
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
This should do it. Tested in the simulator, as well as on a device.
finally I have to post my problem here. Yes it could be duplicate question here as I have referred many answers regarding to this question.But I could not find any fix. (Also didn't find any question regarding to tab-bar app specific so...) And I don't want to use MPMoviePlayerViewController
I have tab-bar application. In last tab's VC there is a button. On click event I want to start movie player. For that I am using MPMoviePlayerController. Its all fine when orientation is Portrait . But regarding to changes, now I have to play it in landscape mode only.
here is my code :
-(void)playMovie
{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[self.scrollView addSubview:moviePlayer.view];
[moviePlayer play];
[moviePlayer setFullscreen:TRUE];
}
-(void)btnPlayHandler:(id)sender
{
NSLog(#"btnPlayHandler");
NSURL * videoUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#",[dictResponse valueForKey:#"VideoPath"]]];
moviePlayer = [[MPMoviePlayerController alloc]initWithContentURL:videoUrl];
//[moviePlayer.view setFrame:CGRectMake(20, 40, 280, 222)];
moviePlayer.fullscreen = YES ;
moviePlayer.shouldAutoplay = NO ;
[self performSelector:#selector(playMovie) withObject:nil afterDelay:1];
}
- (void) movieWillExit:(NSNotification *)notification
{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
}
- (void) movieExit:(NSNotification *)notification
{
[moviePlayer stop];
[moviePlayer.view removeFromSuperview];
moviePlayer = nil ;
[btnPlay setHidden:FALSE];
}
- (void)moviePreLoad:(NSNotification *)notification
{
NSLog(#"moviePreLoad");
}
- (void)moviePlaybackComplete:(NSNotification *)notification
{
NSLog(#"moviePlaybackComplete");
[btnPlay setHidden:FALSE];
}
Only device's orientation is changed not player's view ! How to accomplish this ??? Is it because the tab-bar application ?
You are making the window landscape, but as you have set the MPMoviePlayer view in scrollview, it is not rotating. try to rotate the scrollview according to your orientation.
See this link http://blog.sallarp.com/shouldautorotatetointerfaceorientation/. Hopefully it will solve your problem.
I am using AVPlayer to play online movie. It is working fine. Now the problem is, when I pop out from the view before the movie starts playing, the background process keeps running. And when the movie gets loaded, it starts playing in background.
I tried to release the player in viewWillDisappear. But its not working.
- (void)viewWillDisappear:(BOOL)animated
{
if (self.player.currentItem.status != AVPlayerItemStatusReadyToPlay)
{
[self.player.currentItem removeObserver:self forKeyPath:kRateKey];
[self.player.currentItem removeObserver:self forKeyPath:kStatusKey];
[self.player.currentItem removeObserver:self forKeyPath:kTimedMetadataKey];
}
[self.player pause];
[self.player release];
[self.playerLayerView release];
[super viewWillDisappear:animated];
}
Can anyone please help?
Thanks in advance
- (void)viewWillDisappear:(BOOL)animated
if (self.player.playing==YES) {
[self.player stop];
self.player=nil;
}
}
This might help..
if (self.player.currentItem.playbackLikelyToKeepUp == 1 ) {
NSLog(#"Ready To Play");
[self.player play];
}else{
NSLog(#" Show HUD Buffering...");
[self.player pause];
}
Suppose user taps on a button and video begins to play. Now when video plays, it always in full screen mode.
Video should be played in a portrait mode (but normally video is played in landscape mode). How can I do this?
Just an update, the latest iPhone SDK 3.2+ will now allow the programmers to show the video in any desired size and Orientation, New MPMoviePlayerView is provided, which is a property of MPMoviePlayerController, this view will have the video, which you can add as a subview to your view.
#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];
}
This Solution will be rejected by Apple, as setOrientation for movie player is the Private API. You need to be careful, but it may work on Jailbroke iPhones.
From the documented docs i do not think this is possible using the built in media player
Try this out.
I found something new.
#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];
}
Here's what I did. Add NSNotification to notify you when preloading of the video finishes.
- (void)playVideoUrl:(NSString *)videoUrl {
NSURL *url = [NSURL URLWithString:videoUrl];
MPMoviePlayerController* theMovie=[[MPMoviePlayerController alloc]
initWithContentURL:url];
[[NSNotificationCenter defaultCenter] addObserver:self
//MPMoviePlayerContentPreloadDidFinishNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMovieFinishedPreloading:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:theMovie];
// Movie playback is asynchronous, so this method returns immediately.
[theMovie play];
}
Callback selector:
-(void)myMovieFinishedPreloading:(NSNotification*)aNotification {
NSArray *windows = [[UIApplication sharedApplication] windows];
UIWindow *moviePlayerWindow = nil;
if ([windows count] > 1)
{
moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
}
CGAffineTransform transform = CGAffineTransformMakeScale(0.5, 0.5);
transform = CGAffineTransformRotate(transform, -90.0f*M_PI/180.0f);
[moviePlayerWindow setTransform:transform];
}