0

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.")
    }

}

Cull
  • 23
  • 5
  • Instantiate an `AVRoutePickerView` in your view hierarchy then assign the `AVRoutePickerView`'s `player` property to your `AVPlayer` instance. You'll then have a GUI for choosing the AirPlay route(s) for your `AVPlayer`. I'm just starting to investigate assigning the route(s) in code without the GUI but, as you have also found, I'm seeing little documentation for macOS. If you've found a solution, please post it as an answer to your own question. – Jon May 30 '20 at 18:26
  • thanks for that info man, ill check out the AVRoutePickerView GUI and post back how it goes. Yeh the documentation is abysmal. – Cull May 31 '20 at 06:58
  • gave this a try with the code below to instantiate a AVRoutePicker on button press. GOOD NEWS: the picker appears, BAD NEWS: No confirmed airplay devices, or USB DACs on my system show this way but 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) } – Cull May 31 '20 at 23:18
  • You have to assign an active `AVPlayer` instance to the `AVRoutePickerView` instance's `player` property for the AirPlay route(s) to appear, – Jon Jun 01 '20 at 02:35
  • correctamundo! now I see them the list. This has been for a personal project to manage a large local library of music files. Thanks for your help! – Cull Jun 01 '20 at 05:32
  • Glad that’s working for you. I’m going to add my comment as an answer that you can mark as accepted. – Jon Jun 01 '20 at 11:30
  • done! thanks again :) – Cull Jun 02 '20 at 22:49

1 Answers1

2

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.

Jon
  • 1,469
  • 1
  • 14
  • 23