Search code examples
swiftmacosaudioavplayerairplay

Directing Audio to Air Play Speakers from a MacOS Swift Application Using AVPlayer


Im trying to direct audio from a MacOS application I've written in SWIFT directly to air play speakers.

I can direct audio to onboard sound outputs or say a USB DAC no problems. I can get audio device ids for say a connected USB DAC and direct audio specifically to that fine within my application but having trouble finding good samples/docs on doing it to airplay speakers with SWIFT/AVPLAYER.

Anyone seen any good docs / code samples on directing audio from a macOS (not ios) swift application to airplay speakers directly? i.e. how to get a list of airplay audio device ids and send output to them from avplayer?

For directing sound output to say a USB DAC I can get a list of playback audio device ids with this function -

func getOutputDevices() -> [AudioDevice] {

print_log ("")
print_log("+++ START AUDIO PLAYBACK DEVICES FOUND....")
print_log ("")

for device in AudioDevice.allOutputDevices() {
    print("DEVICE NAME: " + device.name + " ->  DEVICE ID: " + String(device.uid ?? "Empty Device"))

}

print_log ("")
print_log("+++ END AUDIO PLAYBACK DEVICES FOUND....")
print_log ("")

return AudioDevice.allOutputDevices()

}

and then play audio to one of those returned audio devices like so using avplayer and setting the audio audioOutputDeviceUniqueID -

print_log("Setting audio output device to " + String(playback_device.uid ?? "Empty"))


gbl_queue_player.audioOutputDeviceUniqueID=playback_device.uid


gbl_queue_player.addObserver(stage, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
gbl_queue_player.addObserver(stage, forKeyPath: "rate", options: NSKeyValueObservingOptions.new, context: nil)
gbl_queue_player.addObserver(stage, forKeyPath:"currentItem", options:NSKeyValueObservingOptions.new, context:nil)


gbl_queue_player.play()

I hope that all makes sense. Ive found some docs pertaining to IOS but not MacOS.

Thanks!

EDIT 01/07/2020

Gave this a try with the code below to instantiate a AVRoutePicker on button press.

GOOD NEWS: the picker appears and the builtin speakers are listed as an option.

BAD NEWS: No confirmed airplay devices, or USB DACs on my system show this way but they do in OS' sound picker.

@IBAction func airplay_button_clicked(_ sender: Any)

{

let frame = CGRect(x: 50, y: 50, width: 100, height: 50)

gbl_routePickerView = AVRoutePickerView(frame: frame)

view.addSubview(gbl_routePickerView) }

}

There's no sandboxing on this application.

*** EDIT SOLUTION 01/06/2020

As pointed out by Jon a player must be assigned to the route pickers player property

so the function for me became ....

@IBAction func airplay_button_clicked(_ sender: Any) {

    print_log("Airplay button clicked")

    let frame = CGRect(x: 50, y: 50, width: 100, height: 50)



    if #available(OSX 10.15, *) {
        gbl_routePickerView = AVRoutePickerView(frame: frame)
        gbl_routePickerView.player=gbl_queue_player
        view.addSubview(gbl_routePickerView)

    } else {
        // Fallback on earlier versions
        print_log("OS Does not support avroute picker.")
    }

}


Solution

  • Instantiate an AVRoutePickerView in your view hierarchy then assign your AVPlayer instance to the AVRoutePickerView's player property. You'll then have a GUI for choosing the AirPlay route(s) for your AVPlayer.