However I implement the proper MPRemoteCommandCenter functions, the playback buttons are not responsive at all in carplay app.
(It works correctly with CarPlay before iOS 14, using MPPlayableContentManager)
Non of the MPRemoteCommandCenter callbacks are being called.
What could be the reason?
The code where I set up remote command center:
func setupRemoteCommandCenterTargets() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget {event in
//myPlayer.play()
return .success
}
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget {event in
//myPlayer.pause()
return .success
}
commandCenter.nextTrackCommand.isEnabled = true
commandCenter.nextTrackCommand.addTarget {event in
//myPlayer.next()
return .success
}
commandCenter.previousTrackCommand.isEnabled = true
commandCenter.previousTrackCommand.addTarget {event in
//myPlayer.prev()
return .success
}
}
I know this question is old but I came across the same problem recently. What I deduced is that when MPNowPlayingInfoPropertyIsLiveStream is set to true, then the audio is considered a live stream and, in that case, you shouldn't use the pause button from the control center but the stop button instead.
In fact, if you implement the stop command:
commandCenter.stopCommand.addTarget { [unowned self] event in
// myPlayer.pause()
return .success
}
The control center changes on the iPhone too, not just on CarPlay, and the button for live streams become "stop".
I have no idea why on CarPlay the button is "stop" by default instead of pause, but this will fix your issue.
Related
I have an issue which has been bugging me for few months now.
I have a running Timer which prints the AVAudioPlayer's currentTime, it all goes well unless I
pause the player
minimise the app or lock the screen
resume playback
The time that is printed is suddenly constant. Sound is properly playing but the currentTime is not changing anymore unless I pause the player and call play() again.
Also, the worst part is that it does not happen every time, sometimes I have to repeat those steps 2/3 times but the bug happens eventually.
I have read the documentation and searched for some answers but there is nothing.
private func setTimer(_ on: Bool) {
guard on else {
timer?.invalidate()
timer = nil
return
}
guard timer == nil else { return }
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { _ in
self.currentPlaybackTime = self.player.currentTime
print("Delegate newTime: \(self.player.currentTime)")
})
}
func play() {
print("Tries playing")
guard player.prepareToPlay(),
player.play() else {
playbackState = .failed
return
}
print("Playing OK")
setTimer(true)
playbackState = .playing
}
Console prints
Delegate newTime: 68.74916099773243 Delegate newTime:
68.74916099773243 Delegate newTime: 68.74916099773243 Delegate newTime: 68.74916099773243 Delegate newTime: 68.74916099773243
Delegate newTime: 68.74916099773243 ...
EDIT: player.isPlaying returns false when the time stays the same, it returns correctly true when time is correctly increasing
Why would AVAudioPlayer do this to me? It's clear that the timer works in the background and player causes the issue here.
The solution is to deactivate and activate the 'AVAudioSession'.
When the app goes in to the background and when the playback is stopped iOS is setting the AVAudioSession's active state to false so you need to set it back to true if you use the lock screen controls to restart playback.
What I did (following the advice from Adam in the comment above) is set the false status when I stop playback then set to true when the user starts playback from the lock screen controls or via the play button on earbuds.
do
{
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
}
catch
{
NSLog(error.localizedDescription)
}
I have tested this in my own app and after weeks of attempts the functionality is now correct.
I am handling audio playback using AVAudioEngine and AVAudioPlayerNode in my app, and I want to implement remote controls. Background audio is configured and working.
Control center controls work, but the play/pause button does not update when I play/pause the music from inside the app. I am testing on a real device.
Control center screenshot
Here is my AVAudioSession setup code:
func setupAudioSession() {
UIApplication.shared.beginReceivingRemoteControlEvents()
do {
try AVAudioSession.sharedInstance().setActive(true)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch let sessionError {
print("Failed to activate session:", sessionError)
}
}
MPRemoteCommandCenter setup:
func setupRemoteControl() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
self.audioPlayerNode.play()
return .success
}
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
self.audioPlayerNode.pause()
return .success
}
}
Lock screen controls - never appeared.
So here is the solution to my problem, I was starting my AVAudioEngine together with its setup function called from viewDidLoad(), that was the issue, and i used .play()/.pause() methods on my AVAudioPlayerNode to manipulate the audio, however AVAudioPlayerNode does not emit master audio, outputNode of AVAudioEngine does.
So whenever you want to play/pause audio from inside your app or from command center, if you are using AVAudioEngine to handle audio in you application, don’t forget to call .stop()/.start() methods on your AVAudioEngine. Lock screen controls should show up and play/pause buttons should update properly in command center/lock screen even without a single property set to MPNowPlayingInfoCenter.default().nowPlayingInfo.
MPRemoteCommandCenter setup:
func setupRemoteControl() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
try? self.audioEngine.start()
return .success
}
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
self.audioEngine.stop()
return .success
}
}
I am using AudioKit to manage my sound engine in my app. I think AudioKit's settings are overriding something and I can't get lock screen audio controls. This is the code I have currently:
//Configure media control center
UIApplication.shared.beginReceivingRemoteControlEvents()
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
//Update your button here for the pause command
return .success
}
commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
//Update your button here for the play command
return .success
}
I have this in a function which sets up all of my AudioKit settings but it doesn't work. Is there a special procedure for lock screen audio controls with AudioKit?
you have to write these code in appdelegate to implement :
do {
try AVAudioSession.sharedInstance().setActive(true)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}catch{
}
UIApplication.shared.beginReceivingRemoteControlEvents()
Keep in mind it's important to disable .mixWithOthers
You have to rewrite this methord:
//MARK:-音乐播放相应后台状态
override func remoteControlReceived(with event: UIEvent?) {
if (event?.type == .remoteControl) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: Configs.remotePlayCotrolNotificaation), object: nil, userInfo: ["event":event!])
}
}
I am creating a music player (In Swift 3) that uses MPMediaItems and MPMediaPlayerController. I cannot for the life of me figure out how to control music from the lock screen or notification center...
I have read every article I can find on MPRemoteCommandCenter and MPNowPlayingInfoCenter and I cannot get it to work.
I have enabled background music playback, currently the music continues playing outside of the app, but does not received remote commands.
Below is the code currently being used
In my View Did Load I call the following function
let player = MPMusicPlayerController.applicationMusicPlayer()
let commandCenter = MPRemoteCommandCenter.shared()
func configureCommandCenter() {
print("Enter configuration")
self.commandCenter.playCommand.addTarget { [weak self] event -> MPRemoteCommandHandlerStatus in
guard let sself = self else { return .commandFailed }
print("Play")
sself.player.play()
self?.getNowPlayingItem()
return .success
}
self.commandCenter.pauseCommand.addTarget { [weak self] event -> MPRemoteCommandHandlerStatus in
guard let sself = self else { return .commandFailed }
print("Pause")
sself.player.pause()
self?.getNowPlayingItem()
return .success
}
self.commandCenter.nextTrackCommand.addTarget { [weak self] event -> MPRemoteCommandHandlerStatus in
guard let sself = self else { return .commandFailed }
print("next")
sself.player.skipToNextItem()
self?.getNowPlayingItem()
return .success
}
self.commandCenter.previousTrackCommand.addTarget { [weak self] event -> MPRemoteCommandHandlerStatus in
guard let sself = self else { return .commandFailed }
print("Prev")
sself.player.skipToPreviousItem()
self?.getNowPlayingItem()
return .success
}
}
To reiterate my project compiles fine, plays media, continues playing media when app is not in focus and when phone is locked, however no commands are seen from within the app, resulting in the app not being able to be controlled from the lock screen or notification center. Any help would be greatly appreciated.
I would also like to mention that I have looked at the Apple API Docs related to both RemoteCommands and InfoCenter.
Am I missing some key step in order to get remote commands registering from within the app?
The problem is that your player is MPMusicPlayerController.applicationMusicPlayer(). You cannot use the application music player as a remote control target.
If you want remote control target capabilities, you need your player to be something like an AVAudioPlayer.
I want to build a radio app and so I would like to use the stop button instead of the pause button in the control center like Apple Radio does in the native music app :
Here is what I did in my RadioPlayer class :
private var shoutcastStream = NSURL(string: "http://shoutcast.com:PORT/;stream.mp3")
var playerItem:AVPlayerItem?
var player:AVPlayer?
let commandCenter = MPRemoteCommandCenter.sharedCommandCenter()
override init() {
super.init()
do {
// Allow background audio
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ as NSError {
}
// Disable Next, Prev and Pause
commandCenter.pauseCommand.enabled = false
commandCenter.nextTrackCommand.enabled = false
commandCenter.previousTrackCommand.enabled = false
// Enable Play
commandCenter.playCommand.enabled = true
commandCenter.playCommand.addTarget(self, action: #selector(RadioPlayer.play))
// Enable Stop
commandCenter.stopCommand.enabled = true
commandCenter.stopCommand.addTarget(self, action: #selector(RadioPlayer.stop))
} catch _ as NSError {
}
}
It's now working fine but the stop button isn't showing. Instead, I have the Pause button, which doesn't make sense for a radio player haha.
Note that in the above case, even if the control center is showing the pause button, nothing happens when pause button is pressed, because no target is attached to it (I attached it to the stopCommand).
So the question is: how to use that Stop button? Thank you.
EDIT:
I think the "stop" command is only displayed when MPNowPlayingInfoPropertyIsLiveStream = true (available only from iOS 10) /:
It does not matter if you disable the "pause" or "togglePlayPause" commands. From iOS 10, the "stop" command will be displayed if MPNowPlayingInfoPropertyIsLiveStream = true.
You may need to handle the "pause" or the "togglePlayPause" command too (for earlier versions). Good luck!
OK, I also had this doubt and did not find on the internet how to do what I wanted so I started reading more about MPRemoteCommandCenter and MPNowPlayingInfoCenter.
I tried disabling all the buttons I did not use. Also, I read about MPNowPlayingInfoPropertyIsLiveStream and I share in this post in case anyone finds it useful (look at the comments in the code):
Swift 3
MPNowPlayingInfoCenter (for metadata):
var songInfo = [:] as [String : Any]
if NSClassFromString("MPNowPlayingInfoCenter") != nil {
songInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(image: UIImage(named: "your_image_name")!)
songInfo[MPMediaItemPropertyTitle] = "Title"
songInfo[MPMediaItemPropertyArtist] = "Artist name"
// If is a live broadcast, you can set a newest property (iOS 10+): MPNowPlayingInfoPropertyIsLiveStream indicating that is a live broadcast
if #available(iOS 10.0, *) {
songInfo[MPNowPlayingInfoPropertyIsLiveStream] = true
} else {
// Fallback on earlier versions
}
MPNowPlayingInfoCenter.default().nowPlayingInfo = songInfo
} // end if MPNowPlayingInfoCenter
MPRemoteCommandCenter:
if #available(iOS 9.1, *) {
let center = MPRemoteCommandCenter.shared()
// Disable all buttons you will not use (including pause and togglePlayPause commands)
[center.pauseCommand, center.togglePlayPauseCommand, center.nextTrackCommand, center.previousTrackCommand, center.changeRepeatModeCommand, center.changeShuffleModeCommand, center.changePlaybackRateCommand, center.seekBackwardCommand, center.seekForwardCommand, center.skipBackwardCommand, center.skipForwardCommand, center.changePlaybackPositionCommand, center.ratingCommand, center.likeCommand, center.dislikeCommand, center.bookmarkCommand].forEach {
$0.isEnabled = false
}
// For "play" command
center.playCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
// play the song here
return MPRemoteCommandHandlerStatus.success
}
// For "stop" command
center.stopCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
// stop the song here
return MPRemoteCommandHandlerStatus.success
}
} else {
// Fallback on earlier versions
}
I have done. I hope I have helped you and others (:
according to this question-answer , Apparently ControlCenter isn't customizable(At least until now).