I am using AVPlayerLayer to play video and playing the audio of video when app goes in background. Is there any way I can manage controls when screen is locked. User wants to stop play the audio.
My app is working in background but controls are not visible so user have to open the app and stop the video.
I got the solution simply used MPRemoteCommandCenter and added details about video see code in Objetive C
// to setup the playing info
- (void) setupNowPlayingInfoCenter{
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.togglePlayPauseCommand setEnabled:YES];
[commandCenter.playCommand setEnabled:YES];
[commandCenter.pauseCommand setEnabled:YES];
[commandCenter.nextTrackCommand setEnabled:NO];
[commandCenter.previousTrackCommand setEnabled:NO];
[commandCenter.playCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
[self->_player play];
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.pauseCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
[self->_player pause];
return MPRemoteCommandHandlerStatusSuccess;
}];
}
// to update the playing info when enter in background
- (void) updateNowPlayingInfoCenter {
NSDictionary *metadata = [self.media metaData];
MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
[songInfo setObject:metadata[MIBMediaMetaDataTrackNameKey] forKey:MPMediaItemPropertyTitle];
[songInfo setObject:metadata[MIBMediaMetaDataTrackNameKey] forKey:MPMediaItemPropertyArtist];
[songInfo setObject:metadata[MIBMediaMetaDataTrackDurationKey] forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:[NSNumber numberWithDouble:(!self.playing ? 0.0f : 1.0f)] forKey:MPNowPlayingInfoPropertyPlaybackRate];
[playingInfoCenter setNowPlayingInfo:songInfo];
}
// add this line of code in init or viewdidload
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(applicationDidEnterBackgroundNotification:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
Called these 2 methods when enter in background
- (void)applicationDidEnterBackgroundNotification:(NSNotification *)notification {
[self setupNowPlayingInfoCenter];
[self updateNowPlayingInfoCenter];
}
I am playing audio from url in AVAudioPlayer. I am trying to control the audio from the control centre and from the lock screen. I am able to control seek and forward, backward functionality but i am unable to control Play/Pause functions from control centre.
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
MPRemoteCommandCenter *remoteCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[[remoteCommandCenter skipForwardCommand] addTarget:self action:#selector(skipForward)];
[[remoteCommandCenter togglePlayPauseCommand] addTarget:self action:#selector(togglePlayPause)];
[[remoteCommandCenter skipBackwardCommand] addTarget:self action:#selector(skipBackward)];
MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
[songInfo setObject:#"your song" forKey:MPMediaItemPropertyTitle];
[songInfo setObject:#"your artist" forKey:MPMediaItemPropertyArtist];
[songInfo setObject:#"your album" forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:#"0" forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[songInfo setObject:#"600" forKey:MPMediaItemPropertyPlaybackDuration];
[playingInfoCenter setNowPlayingInfo:songInfo];
I need some help with an issue when my music player app is playing on background.
I'm able to play the musics in the app and in the background with both services. I'm also able to set the MPNowPlayingInfoCenter and it displays the correct info, but the play/pause, next track and previous tracks are working only when the user is authenticated with Spotify:
notifications are received correctly by the app when the user authenticates with Spotify
but it doesn't work when user authenticates with Apple Music. In this case it seems that Apple Music is the one receiving the notifications.
I'm using an AVPlayer to play the musics when synced with Apple Music and SPTAudioStreamingController when synced with Spotify.
Here is the code of the Media center setup:
- (void)setMediaCenterinfoForPlayer:(id)player {
SPTAudioStreamingController *spotifyPlayer;
AVPlayer *localPlayer;
NSMutableDictionary *trackInfo = [[NSMutableDictionary alloc] initWithDictionary: #{ MPMediaItemPropertyTitle: self.currentTrack.name,
MPMediaItemPropertyArtist: ((SPTArtist *)self.currentTrack.artists[0]).name,
MPMediaItemPropertyAlbumTitle : self.currentTrack.album.name,
MPNowPlayingInfoPropertyPlaybackRate: #(1.0)
}];
if ([player isKindOfClass:[SPTAudioStreamingController class]]) {
spotifyPlayer = (SPTAudioStreamingController *)player;
[trackInfo setObject:[NSNumber numberWithFloat:spotifyPlayer.currentPlaybackPosition] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[trackInfo setObject:[NSNumber numberWithFloat:spotifyPlayer.currentTrackDuration] forKey:MPMediaItemPropertyPlaybackDuration];
} else {
localPlayer = (AVPlayer *)player;
NSTimeInterval playbackTime = [self currentPlaybackTimeForPlayer:player];
[trackInfo setObject:#(playbackTime) forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[trackInfo setObject:#(CMTimeGetSeconds(localPlayer.currentItem.asset.duration)) forKey:MPMediaItemPropertyPlaybackDuration];
}
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = trackInfo;
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Class playingInfoCenter = NSClassFromString(#"MPNowPlayingInfoCenter");
if (playingInfoCenter) {
[self albumURLCoverForCurrentTrackWithBlock:^(UIImage *albumImage) {
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:albumImage];
[trackInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:trackInfo];
}];
}
}
Here is the code for the event handling:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary *playingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo];
[playingInfo setObject:[NSNumber numberWithFloat:[AudioPlayerManager sharedInstance].spotifyPlayer.currentPlaybackPosition] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
center.nowPlayingInfo = playingInfo;
if (event.subtype == UIEventSubtypeRemoteControlPlay) {
[[AudioPlayerManager sharedInstance] playTrack];
} else if (event.subtype == UIEventSubtypeRemoteControlPause) {
[[AudioPlayerManager sharedInstance] pauseTrack];
} else if (event.subtype == UIEventSubtypeRemoteControlPreviousTrack) {
[[AudioPlayerManager sharedInstance] previousTrack];
} else if (event.subtype == UIEventSubtypeRemoteControlNextTrack) {
[[AudioPlayerManager sharedInstance] nextTrack];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kEventTypeRemoteControlUpdateState object:self];
}
}
Could anyone please point me a way to tackle this situation?
I want to display playback(seekbar, next button & prev button) and Song name , Artist name ,Image etc. on lock screen if song is playing and user locked his iPhone. But I got one issue. Issue is the MPNowPlayingInfoCenter info value display once for 1-2 sec after that all value will disappeared automatically and after that it is display default value.
This below screenshot1 is perfect but after 1-2 this all value disappeared see the screenshot2 all values are disappeared
-(void)SetLockScreenView :(NSString *)SongName ArtistName:(NSString *)ArtistName AlbumTitle:(NSString *)AlbumTitle DisplayImg:(NSString *)displayImg
{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Class playingInfoCenter = NSClassFromString(#"MPNowPlayingInfoCenter");
if (playingInfoCenter) {
if(displayImg.length>0 && displayImg!=nil)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:displayImg]];
dispatch_async(dispatch_get_main_queue(), ^{
if(data!=nil)
{
UIImage *image = [UIImage imageWithData:data];
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: image];
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
}
else
{
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: [UIImage imageNamed:#"artistDefaultImg"]];
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
}
[songInfo setObject:SongName forKey:MPMediaItemPropertyTitle];
[songInfo setObject:ArtistName forKey:MPMediaItemPropertyArtist];
[songInfo setObject:AlbumTitle forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:[NSNumber numberWithFloat:self.manager.currentItem.duration] forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:[NSNumber numberWithInt:0] forKey:MPNowPlayingInfoPropertyPlaybackRate];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
NSLog(#"songInfo :%#",songInfo);
});
});
}
}
}
#pragma mark - Remote Control
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
[self.manager.player play];
break;
case UIEventSubtypeRemoteControlPause:
[self.manager.player pause];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
if ([self.manager.player isPlaying]) {
[self.manager.player pause];
}
else {
[self.manager.player play];
}
break;
case UIEventSubtypeRemoteControlNextTrack:
[self Onclick_next:self];
NSLog(#"Next song play");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[self Onclick_prev:self];
NSLog(#"Prev song play");
break;
default:
break;
}
}
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
How do i pass the song info such as song name and track duration to control center.
Player plays music fine and the play and pause control works fine though.
Playing with apple's music app
Playing with my app with the code below, how to pass song information in order to display it ?
//AppDelegate
-(void)setupAudio
{
// Set AVAudioSession
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
// Change the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
//if it is a remote control event handle it correctly
if (event.type == UIEventTypeRemoteControl) {
if (event.subtype == UIEventSubtypeRemoteControlPlay)
{
NSLog(#"UIEventSubtypeRemoteControlPlay");
[[AppMusicPlayer sharedService]playAudio];
}
else if (event.subtype == UIEventSubtypeRemoteControlPause)
{
NSLog(#"UIEventSubtypeRemoteControlPause");
[[AppMusicPlayer sharedService]pauseAudio];
}
else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause)
{
NSLog(#"UIEventSubtypeRemoteControlTogglePlayPause");
}
}
}
AppMusicPlayer.m
+ (id) sharedService
{
static dispatch_once_t _singletonPredicate;
static AppMusicPlayer *_sharedObject = nil;
dispatch_once(&_singletonPredicate, ^{
_sharedObject = [[AppMusicPlayer alloc]init];
});
return _sharedObject;
}
- (id)init
{
self = [super init];
if (self) {
// Work your initialising here as you normally would
}
return self;
}
- (void)playAudio
{
[self.audioPlayer play];
}
- (void)pauseAudio
{
NSLog(#"pause");
[self.audioPlayer pause];
}
- (void)playAudioFromURL:(NSURL *)songURL
{
[self.audioPlayer stop];
//Declare the audio file location and settup the player
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:songURL error:&error];
[self.audioPlayer setNumberOfLoops:-1];
if (error)
{
NSLog(#"%#", [error localizedDescription]);
}
else
{
//Load the audio into memory
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
}
}
-(void)setUpRemoteControl
{
NSDictionary *nowPlaying = #{MPMediaItemPropertyArtist: songItem.artistName,
MPMediaItemPropertyAlbumTitle: songItem.albumTitle,
MPMediaItemPropertyPlaybackDuration:songItem.playbackDuration,
MPNowPlayingInfoPropertyPlaybackRate:#1.0f,
MPMediaItemPropertyArtwork:[songMedia valueForProperty:MPMediaItemPropertyArtwork]
};
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nowPlaying];
}
Are you playing songs from iPods music library ?
If yes than You should you MPMusicPlayerViewController, it provides in-build functionality for seek bar properties as well as Info center.
I can see here in your code you have used AVAudioPlayer,
We can set only below details using AVAudioPlayer class, but can't access seekbar change event in our app.
NSMutableDictionary *albumInfo = [[NSMutableDictionary alloc] init];
UIImage *artWork = [UIImage imageNamed:album.imageUrl];
[albumInfo setObject:album.title forKey:MPMediaItemPropertyTitle];
[albumInfo setObject:album.auther forKey:MPMediaItemPropertyArtist];
[albumInfo setObject:album.title forKey:MPMediaItemPropertyAlbumTitle];
[albumInfo setObject:artworkP forKey:MPMediaItemPropertyArtwork];
[albumInfo setObject:album.playrDur forKey:MPMediaItemPropertyPlaybackDuration]
[albumInfo setObject:album.elapsedTime forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:albumInfo]
Here we need to count album.playrDur and album.elapsedTime as per required format.
You are looking for this:
- (IBAction)playButtonTouched:(id)sender {
Class playingInfoCenter = NSClassFromString(#"MPNowPlayingInfoCenter");
if (playingInfoCenter) {
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: [UIImage imagedNamed:#"AlbumArt"]];
[songInfo setObject:#"Audio Title" forKey:MPMediaItemPropertyTitle];
[songInfo setObject:#"Audio Author" forKey:MPMediaItemPropertyArtist];
[songInfo setObject:#"Audio Album" forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
}
}
I happen to be working on the same thing today and don't know the answer, but in my app the Control Center does show the app name. I want to be able to show the title of the thing I'm playing. I even have a picture I'd like to show, like an album cover. I don't think I'm doing anything different than you, I didn't code anything to send it the app name, for example.
Wait a second... I see there's something called MPNowPlayingInfoCenter that we should look into..
Not sure but may be this code help you little bit..
- (void) handle_NowPlayingItemChanged: (id) notification
{
MPMediaItem *currentItem = [musicPlayer nowPlayingItem];
NSString *titleString = [currentItem valueForProperty:MPMediaItemPropertyTitle];
if (titleString)
{
titleLabel.text = [NSString stringWithFormat:#"Title: %#",titleString];
}
else
{
titleLabel.text = #"Title: Unknown title";
}
}