Get Name of Airplay Device Using Avplayer

Get name of AirPlay device using AVPlayer

After searching in other frameworks to get the name of the Apple TV you're connected to, I finally found this information in the AudioToolbox framework. There may be other ways to get this, but so far I have not found another way. Hope this helps.

You'll need to import the AudioToolbox framework:

#import <AudioToolbox/AudioToolbox.h>

and then a method to call when you want to detect if airplay is available

- (BOOL)isAirplayActive {
CFDictionaryRef currentRouteDescriptionDictionary = nil;
UInt32 dataSize = sizeof(currentRouteDescriptionDictionary);
AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, ¤tRouteDescriptionDictionary);

self.deviceOutputType = nil;
self.airplayDeviceName = nil;

if (currentRouteDescriptionDictionary) {
CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs);
if(CFArrayGetCount(outputs) > 0) {
CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0);

//Get the output type (will show airplay / hdmi etc
CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type);

//If you're using Apple TV as your ouput - this will get the name of it (Apple TV Kitchen) etc
CFStringRef outputName = CFDictionaryGetValue(currentOutput, @"RouteDetailedDescription_Name");

self.deviceOutputType = (NSString *)outputType;
self.airplayDeviceName = (NSString *)outputName;

return (CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo);
}
}
return NO;
}

How to get the AirPlay device name without using iOS7 deprecated methods

Starting from iOS6 AudioSession exposes currentRoute to you, which allows retrieving it's port information as well as listening to audioRouteChangeNotification.
(so you no longer need to use C-based API from AudioToolbox framework):

NSString* airplayName = [self activeAirplayOutputRouteName];
if (airplayName) {
//airplay is active

}

(what you want to get is the portDescription of currentAudioRoute):

- (NSString*)activeAirplayOutputRouteName
{
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
AVAudioSessionRouteDescription* currentRoute = audioSession.currentRoute;
for (AVAudioSessionPortDescription* outputPort in currentRoute.outputs){
if ([outputPort.portType isEqualToString:AVAudioSessionPortAirPlay])
return outputPort.portName;
}

return nil;
}

Airplay with AVPlayer?

It's not possible in iOS4, but will be in iOS5.

iOS Airplay - Playing on your TV

This is how I implemented it. It works like a charm !

//Airplay constants

private var observerContextAirplay = 1

private var propertyToObserveAirplay = "externalPlaybackActive"

// MARK: AirPlay Key-value Observing

func startObservingForAirPlayStatusChanges()
{
self.player.moviePlayer.addObserver(self, forKeyPath: propertyToObserveAirplay, options: .New, context: &observerContextAirplay)
}

func stopObservingForAirPlayStatusChanges()
{
self.player.moviePlayer.removeObserver(self, forKeyPath: propertyToObserveAirplay)
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &observerContextAirplay {
if self.player.moviePlayer.externalPlaybackActive == true
{
self.setUpAirPlayView()
}
else if self.player.moviePlayer.externalPlaybackActive == false
{
self.resetPlayerView()
}

}
else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}

//Setup AirplayView

func setUpAirPlayView()
{
//Airplay Button

if self.airPlay_PlayBtn == nil
{
let playImage = UIImage(named: "player_play.png")!
if let _ = self.airPlay_PlayBtnFrame
{
self.airPlay_PlayBtn = UIButton(frame: self.airPlay_PlayBtnFrame)
self.airPlay_PlayBtn.setBackgroundImage(playImage, forState: UIControlState.Normal)
self.airPlay_PlayBtn.addTarget(self, action: #selector(ICFPlayerViewController.airPlayButtonAction), forControlEvents: UIControlEvents.TouchUpInside)

self.airPlay_PlayBtn.setBackgroundImage(UIImage(named: "player_play.png"), forState: .Normal)
self.airPlay_PlayBtn.setBackgroundImage(UIImage(named: "player_pause.png"), forState: .Selected)
self.airPlay_PlayBtn.center = self.view.center

self.view.addSubview(self.airPlay_PlayBtn)
if let _ = self.player
{
self.player.playPauseButton.hidden = true
}
}
}
else
{
self.airPlay_PlayBtn.hidden = false
if let _ = self.player
{
self.player.playPauseButton.hidden = true
}
}

// Airplay Label
if self.airPlayLabel == nil
{
self.airPlayLabel = UILabel()
self.airPlayLabel.frame = CGRectMake(0, 0, 280, 20)

self.airPlayLabel.text = "Your video is now playing on Apple TV"
self.airPlayLabel.textAlignment = NSTextAlignment.Center
self.airPlayLabel.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.6)

self.airPlayLabel.textColor = UIColor.whiteColor()

self.airPlayLabel.sizeToFit()

self.airPlayLabel.center = self.view.center

self.airPlayLabel.center.y = self.view.center.y - self.activityIndicator.frame.size.height*1.5
self.view.addSubview(self.airPlayLabel)
}
else
{
self.airPlayLabel.hidden = false
}

// Thumbnail
self.setupContentThumbnailImageView() //Fetch Thumbnail image
if let _ = self.thumbnailImage
{
self.view.addSubview(self.thumbnailImage)
}

self.view.bringSubviewToFront(bottomToolbar)
self.view.bringSubviewToFront(topToolbar)

if let _ = self.airPlayLabel
{
self.view.bringSubviewToFront(self.airPlayLabel)
}
if let _ = self.airPlay_PlayBtn
{
self.view.bringSubviewToFront(self.airPlay_PlayBtn)
}

}

iOS: Airplay picker MPVolumeView alternative

So the precise answer to my question:

(i) It's not possible to switch audioRoutes programatically with public API except switching to build-in speakers.

[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];

(ii) You can only retrieve a name of airplay-device if it's active AudioRoute. Get name of AirPlay device using AVPlayer

So the practical solution to presenting customised UI Controls for selecting airplay would be:

To customise MPVolumeView, where you can disable volumeSliderand customise routeButton. However you have no other option as picking airplayDevice among list of apple-compatible wireless devices (airPlay, bluetooth, etc) in UIActionSheet that pop ups when you tap on routeButton, but you can observe when user will make a selection there by subscribing to audioRouteChangeNotification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteHasChangedNotification:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];

(also note that if you will plug in/out headphones, it also will trigger this notification)

If you are interested how to retrieve all available audioRoutes and switch programmatically with private API:

MPMediaPlayer framework contains a private class MPAVRoutingController, which allows you exactly that:

Class MPAVRoutingController = NSClassFromString(@"MPAVRoutingController");
Class MPAVRoute = NSClassFromString(@"MPAVRoute");

id routingController = [[MPAVRoutingController alloc] init];
NSArray* availableRoutes = [routingController performSelector:@selector(availableRoutes)];
BOOL isSwitchSuccesful = [[routingController performSelector:@selector(pickRoute:) withObject:availableRoutes.lastObject] boolValue];

(if you want to then access audioRoute info and check if it is Airplay: Detecting airplayRoute)

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

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.



Related Topics



Leave a reply



Submit