8

I'm developing a music application, which should play music in the background.

I use the MPMoviePlayerController to play the music. My code to initiate the MPMoviePlayerController:

NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
resourcePath = [resourcePath stringByAppendingString:@"/music.m4a"];
NSError* err;
self.player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:resourcePath]];
if (err) {
    NSLog(@"ERROR: %@", err.localizedDescription);
}
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
[session setActive:YES error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self.player setShouldAutoplay:NO];
[self.player setControlStyle: MPMovieControlStyleEmbedded];
self.player.view.hidden = YES;
[self.player prepareToPlay];

When I execute [self.player play]; the music starts. But I also want to display the name of the song, the name of the album and the album artwork in the LockScreen and the ControlCenter. I'm using the following code:

Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
    if (playingInfoCenter) {
        NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: [UIImage imageNamed:@"artwork.png"]];
        [songInfo setObject:@"SongName" forKey:MPMediaItemPropertyTitle];
        [songInfo setObject:@"ArtistName" forKey:MPMediaItemPropertyArtist];
        [songInfo setObject:@"AlbumTitle" forKey:MPMediaItemPropertyAlbumTitle];
        [songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
    }

But nothing gets displayed in the LockScreen. It doesn't get displayed in the ControlCenter either.

How can I solve my problem? I didn't find anything on the internet.

Thanks in advance, Fabian.

FTFT1234
  • 435
  • 8
  • 17
  • Is your code actually being executed? – matt May 06 '15 at 18:31
  • Yes, [[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo objectForKey:MPMediaItemPropertyTitle] returns the right SongName. – FTFT1234 May 06 '15 at 18:53
  • Aha! That's really great news. So not only is your code running, but it is actually setting the `nowPlayingInfo` successfully. So let me ask you this - are you first responder, so that you are successfully receiving the remote control events from the lock screen and control center? – matt May 06 '15 at 18:56
  • The remote controls are actually not displayed in the lock screen and I don't think, my app becomes the first responder. – FTFT1234 May 06 '15 at 19:05
  • I think that's the problem, then. You can't influence the lock screen unless you are first responder. – matt May 06 '15 at 19:06
  • Okay, and how I can solve the problem? – FTFT1234 May 06 '15 at 19:08
  • You have to be first responder. And there are other requirements as well. See my answer below, along with the linked material. – matt May 06 '15 at 19:13

4 Answers4

30

The problem is that you are not satisfying the requirements to become master of the lock screen and control center, as I explain in my book. You should be seeing the modern (iOS 8) equivalent of this:

enter image description here

The fact that you are not seeing it suggests that you are omitting one or more of the requirements, which I list (quoting directly from my book here):

  • Your app must contain a UIResponder in its responder chain that returns YES from canBecomeFirstResponder, and that responder must actually be first responder.
  • Some UIResponder in the responder chain, at or above the first responder, must implement remoteControlReceivedWithEvent:.
  • Your app must call the UIApplication instance method beginReceivingRemoteControlEvents.
  • Your app’s audio session’s policy must be Playback.
  • Your app must be emitting some sound.

I don't know which of those you are omitting; it could be more than one. You might like to compare your code with a working example, here:

https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/iOS7bookExamples/bk2ch14p653backgroundPlayerAndInterrupter/backgroundPlayer/backgroundPlayer/ViewController.m

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • You're very welcome. It's a lot of requirements! Omitting one of them is easy, and then things mysteriously don't work. :( – matt May 06 '15 at 19:28
  • Any luck getting this to work when playing through a WKWebView? I can get other sources to change the now playing, just not when playing through a WKWebView. – pir800 Jan 25 '16 at 15:49
  • Is this possible in AVQueuePlayer?means can i add NSMutableDictionary externally in AVplayer for set the artwork ..etc – amisha.beladiya Dec 05 '16 at 12:57
  • @matt that post was really helpful for me and solved my problem, thanks! – Evgeniy Kleban May 05 '17 at 06:00
  • 1
    I verified that my app does do everything in the list, but the lock screen controls and information do not appear when I start the app and play the stream the first time. I have to stop and restart the stream (Icecast) and then on the second attempt the lock screen controls and information appear. The steps on the first attempt and second attempt are exactly the same (i.e. no threading or asynchronous race conditions). So I have no idea why the lock screen is not working correctly on the first attempt. – Ken Roy Oct 15 '17 at 14:44
11

Want to expand @matt's answer - setting nowPlayingInfo is useless when you use AVAudioSessionCategoryOptionMixWithOthers option.

Artem Shmatkov
  • 1,434
  • 5
  • 22
  • 41
  • 1
    Came here to add this - if you add `.mixWithOthers` key you won't be able to set the control center details as you might expect. – owenfi Aug 20 '18 at 04:55
0

Expanding further on @matt's answer, it's required that you call endReceivingRemoteControlEvents and resignFirstResponder when the app comes back into the foreground (applicationDidBecomeActive). Otherwise the OS assumes you are a bad actor (or forgot to turn them off) and turns off your ability to show the sleep controls entirely, even after you call beginReceivingRemoteControlEvents again. I added these calls and now the Sleep Controls always show when they should.

Tyler Sheaffer
  • 1,953
  • 1
  • 16
  • 16
0

Want to explain @zakhej's answer - what's happens when set AVAudioSessionCategoryOptionMixWithOthers.

  1. MPNowPlayingInfoCenter is a singleton, which means can only one application set it.
  2. AVAudioSession is shared with other apps, every app can set it.

so. when you set category AVAudioSessionCategoryAmbient, AVAudioSessionCategoryPlayAndRecord and mix with others, AVAudioSessionCategoryPlayback and mix with others, setting MPNowPlayingInfoCenter's nowPlayingInfo is useless.