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!])
}
}
Related
I'm working on an app that play audio in Apple Watch; All working fine from inside the app.
I'm trying to setup the MPRemoteCommandCenter in 'Now Playing' to change the next/prev track to skipForward/skipBackward and to add a handler for the pause command.
Nothing change in Now Playing commands the the handler not being trigged.
Below a snippet code:
Play method
var player: WKAudioFilePlayer!
#IBAction func play() {
let avSession = AVAudioSession.sharedInstance()
try! avSession.setCategory(.playback, mode: .default, policy: .longForm, options: [])
let url = Bundle.main.url(forResource: "sample", withExtension: "mp3")!
let item = WKAudioFilePlayerItem(asset: WKAudioFileAsset(url: url))
player = WKAudioFilePlayer(playerItem: item)
do {
try avSession.setActive(true, options: .notifyOthersOnDeactivation)
player.play()
} catch {
print("Error")
}
}
Remote controls setup method:
func setupRemoteControls() {
// Get the shared MPRemoteCommandCenter
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.skipForwardCommand.preferredIntervals = [NSNumber(value: 15)]
commandCenter.skipForwardCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
// skip forward
return .success
}
commandCenter.skipForwardCommand.isEnabled = true
commandCenter.skipBackwardCommand.preferredIntervals = [NSNumber(value: 15)]
commandCenter.skipBackwardCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
// skip backword
return .success
}
commandCenter.skipBackwardCommand.isEnabled = true
// Add handler for play Command
commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
self.player.play()
return .success
}
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
self.player.pause()
return .success
}
}
I'm calling self.setupRemoteControls() in awake method. Also I tried to move the setup to the ExtensionDelegate -> applicationDidFinishLaunching
Apple references I used:
https://developer.apple.com/videos/play/wwdc2018/504/
https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/RefiningTheUserExperience/RefiningTheUserExperience.html
:: UPDATE ::
I found when using the AVAudioPlayer instead of WKAudioFilePlayer the setup for MPRemoteCommandCenter working fine.
The problem that I don't use local audio files!.. I stream 'm3u8' files using wowza which worked only with WKAudioFilePlayer.
And if I tried to stream using AVAudioPlayer I get this error
The operation couldn’t be completed. (OSStatus error 2003334207.)
So my issue now to get MPRemoteCommandCenter being configured while still using WKAudioFilePlayer, or find a way to stream using AVAudioPlayer ??
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'm trying to control my audio player from outside of the app,
I started an av audio session but it's not playing on the background(worked fine on swift 3),
do{
myPlayer = AVPlayer(url: url!)
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback
)
do {
try audioSession.setActive(true)
}
}
catch {
print(error)
}
}
my main goal is to control play and pause like this:
So you are building an app that plays audio on the background using AVPlayer. You should use MPNowPlayingInfoCenter to display the song's metadata on the Lock Screen and Control Center, and use MPRemoteCommandCenter to control the previous/next/play/pause actions on the lock screen and control center.
Enable Background Mode for Audio, AirPlay and Picture in
Picture in your Target > Capabilities.
If you're streaming audio from the web, enable Background Mode for Background Fetch too.
Import AVKit and MediaPlayer dependencies.
Setup your AVPlayer with an AVPlayerItem:
guard let url = URL(string: "http://your.website.com/playlist.m3u8") else {
return
}
player = AVPlayer(url: url)
Setup your AVAudioSession:
private func setupAVAudioSession() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
debugPrint("AVAudioSession is Active and Category Playback is set")
UIApplication.shared.beginReceivingRemoteControlEvents()
setupCommandCenter()
} catch {
debugPrint("Error: \(error)")
}
}
Setup the InfoCenter and the RemoteCommandCenter:
private func setupCommandCenter() {
MPNowPlayingInfoCenter.default().nowPlayingInfo = [MPMediaItemPropertyTitle: "Your App Name"]
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.pauseCommand.isEnabled = true
commandCenter.playCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in
self?.player.play()
return .success
}
commandCenter.pauseCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in
self?.player.pause()
return .success
}
}
You can place the setupAVAudioSession() method in your viewDidLoad() method, or in any other place you need it.
If you need to place more info in the MPNowPlayingInfoCenter here's a list of all available properties: General Media Item Property Keys | Apple Developer Documentation
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 use MPRemoteCommandCenter to control my music player app. And now it can play and pause music, but can not play next/previous song, only a poor chance can make it.
When user tap next song button in MPRemoteCommandCenter(e.g in the lock screen), it will call startExtendBGJob() function, then I ask for a thread to do the change song job(I think the bug is here, because I'm don't totally understand the background job's anatomy).
func startExtendBGJob(taskBlock: #escaping () -> Void) {
registerBackgroundTask()
DispatchQueue.global(qos: .userInitiated).async {
DLog("APP into BG")
DispatchQueue.global(qos: .default).async {
taskBlock()
}
while self.isPlaying == false || self.tmpPlayer == nil { // waiting for new avplayer been created.
Thread.sleep(forTimeInterval: 1)
}
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 10) {
self.endBackgroundTask()
}
}
}
func registerBackgroundTask() {
bgIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.endBackgroundTask()
})
assert(bgIdentifier != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(bgIdentifier)
bgIdentifier = UIBackgroundTaskInvalid
isExtendingBGJob = false
DLog("App exit BG!")
}
In startNextPlay() function just finding the next song's url, and prepareToPlay() is for creating a new AVPlayer to play next song.
self.tmpPlayer = AVPlayer(url: streamURL)
I'm not english native spearker, thank you so much to read here if you understand what I'm talking about :]. Any help is welcome.
Sorry, It's my fault. It doesn't need any backgournd job.
Just change something like following:
UIApplication.shared.beginReceivingRemoteControlEvents()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
DLog("fail to set category: \(error)")
}
Make sure it's AVAudioSessionCategoryPlayback