I've looked at MPMoviePlayerController multitasking problem and MPMovieplayerController doesn't play next URL when in background, as well as many other similar questions. None of these solutions have worked for me.
I, like many others, am trying to play a playlist of remote files in the background. I am playing YouTube video files in this case, parsed and played with XCDYouTubeKit. The Kit's XCDYouTubeViewController
handles parsing the YouTube video identifier (the sequence of characters after "watch=" in every YouTube URL) and changing the contentURL
. I have an array of videos in a PlayQueue
singleton, and it plays each of them consecutively correctly when the application is in the foreground. But when it's in the background, the next song loads but it is immediately paused. It plays fine if the user presses play in Control Center or on the lock screen, but I was hoping for a seamless transition between songs.
I have this method that plays the videos (self.moviePlayer is a XCDYouTubeViewController
and self.moviePlayer.moviePlayer is the MPMoviePlayerController
):
- (void)playVideoWithIdenfifier:(NSString*)identifier
{
if ([self checkReachability]) // this simply checks if there's an internet connection
{
if (!self.moviePlayer)
{
self.moviePlayer = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:identifier];
self.moviePlayer.moviePlayer.view.userInteractionEnabled = NO;
self.moviePlayer.moviePlayer.backgroundPlaybackEnabled = YES;
[self.moviePlayer presentInView:self.playerContainer];
[self.moviePlayer.moviePlayer setControlStyle:MPMovieControlStyleNone];
}
else self.moviePlayer.videoIdentifier = identifier
NSError *activationError = nil;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&activationError];
[audioSession setActive:YES error:&activationError];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
[self becomeFirstResponder];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(sliderUpdate) userInfo:nil repeats:YES];
self.moviePlayer.moviePlayer.shouldAutoplay = YES;
[self.moviePlayer.moviePlayer play];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:self.imageView.image];
[songInfo setObject:self.song.title forKey:MPMediaItemPropertyTitle];
[songInfo setObject:[NSString stringWithFormat:@"%@ - Playing from umusio",self.song.album] forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:self.song.artist forKey:MPMediaItemPropertyArtist];
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
[songInfo setObject:[NSNumber numberWithFloat:self.moviePlayer.moviePlayer.currentPlaybackTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[songInfo setObject:[NSNumber numberWithFloat:self.moviePlayer.moviePlayer.duration] forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:[NSNumber numberWithInt:1] forKey:MPNowPlayingInfoPropertyPlaybackRate];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
[self.playButton setImage:[UIImage imageNamed:@"BeamMusicPlayerController.bundle/images/pause.png"] forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:@"BeamMusicPlayerController.bundle/images/pause.png"] forState:UIControlStateSelected];
}
else [[[UIAlertView alloc] initWithTitle:@"error" message:@"reachability check failed" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil] show];
}
When a video ends, it calls this method, which changes the "current song" property and calls checkSongID, which contacts Parse and matches the iTunesSong to a YouTube video identifier. From there, checkSongID calls the previous method, playVideoWithIdentifier:
and passes in the video identifier that it got from Parse. I won't include checkSongID
here, it is not a problem as it works fine in the foreground and doesn't interact with the moviePlayer at all.
- (void)moviePlayBackDidFinish:(NSNotification*)notification
{
iTunesSong* nextSong = [[PlayQueue sharedPlayQueue] nextSong]; // iTunesSong is a custom class that I made.
self.song = nextSong;
[self checkSongID];
}
This all works fine in the foreground, any ideas why it immediately pauses in the background?