Skip to main content

AirPlay

This guide covers how to integrate AirPlay into your React Native application using @quickplay/rn-qp-nxg-player, including rendering the AirPlay button, detecting connection state changes, and querying AirPlay status programmatically.

Platform: iOS only. AirPlay is not supported on Android or tvOS.

Prerequisites

Enable Background Modes for Airplay

Before using any Airplay APIs, enable the required iOS capability in Xcode.

  1. Open your iOS project in Xcode.
  2. Select your app target.
  3. Go to Signing & Capabilities.
  4. Add the Background Modes capability.
  5. Enable Audio, AirPlay, and Picture in Picture.
Background Mode

Overview

AirPlay allows users to wirelessly stream video from an iOS device to an Apple TV or AirPlay-compatible display. The SDK exposes:

FeatureAPI
Native AirPlay route picker buttonQpNxgAirplayView
React to AirPlay connection changesonAirplayConnected callback in PlayerAudioRouteListener
Query if AirPlay is currently activeplayer.isAirplayActive()
Query if any AirPlay route is connecteddeviceInformation.isAirplayRouteConnected()

1. AirPlay & Playback Behavior

The SDK supports AirPlay 2 for both DRM-protected and non-DRM content.

Playback Modes

AirPlay can be started in either of the following ways:

  • Play & Connect – Start playback on the iOS device, then connect to an AirPlay receiver.
  • Connect & Play – Connect to an AirPlay receiver first, then start playback.

DRM Playback

For DRM-protected content, the AirPlay receiver requests its own playback license when playback begins.

Important: AirPlay license authorizations are valid for 30 seconds. If the authorization expires before AirPlay playback starts, your application must refresh the license before providing it to the player.

Player Lifecycle

During AirPlay playback:

  • The receiver device performs the actual playback.
  • The player on the iOS device remains responsible for playback feedback and platform integrations.
  • Disposing the player instance does not stop AirPlay playback.
  • Closing the app stops AirPlay playback.

To ensure AirPlay-related features continue working, keep the player instance alive for the duration of the AirPlay session.

Personalization Support

Features such as bookmarks, stream concurrency, and heartbeat reporting continue to function during AirPlay as long as the player instance remains active on the originating device.

2. AirPlay Button (QpNxgAirplayView)

QpNxgAirplayView is a native iOS AVRoutePickerView wrapped as a React Native component. Tapping it presents the system AirPlay device picker. The button tint color is white by default and is set at the native layer — it cannot be customized via props.

Import

import { QpNxgAirplayView } from '@quickplay/rn-qp-nxg-player';

Render

Render it inside your player controls, guarded by a platform check:

import { Platform } from 'react-native';
import { QpNxgAirplayView } from '@quickplay/rn-qp-nxg-player';

{Platform.OS === 'ios' && (
<QpNxgAirplayView
// add styles if required.
/>
)}

The button automatically reflects the current AirPlay availability and active state using the system's native appearance.


3. AirPlay Button & Player UI Modes

AirPlay button placement depends on which player UI mode is active, controlled by canUseCustomPlayerControls in QPPlayerManager.

When canUseCustomPlayerControls is true — Native Controls

player.playWithNativeControls() presents VideoPlayerViewController — a fully native fullscreen player UI. This screen has its own built-in AirPlay button (AVRoutePickerView) managed entirely by the native layer via airPlayButtonTapped() in VideoPlayerViewController.

No additional wiring is needed from the React Native side.

When canUseCustomPlayerControls is false — Custom RN Controls

QpNxgPlaybackView is rendered inline. There is no built-in AirPlay button — you must add QpNxgAirplayView manually in your player controls UI.

// Add this inside your custom player controls
{Platform.OS === 'ios' && (
<QpNxgAirplayView
// add styles if required.
/>
)}

UI Mode Comparison

Native Controls (canUseCustomPlayerControls: true)Custom RN Controls (canUseCustomPlayerControls: false)
Play API to callplayer.playWithNativeControls()player.play() or player.playInline()
AirPlay ButtonBuilt-in, managed by native layerMust add QpNxgAirplayView manually

4. Listening for AirPlay Connection Changes

Implement the PlayerAudioRouteListener interface and register it on your player instance to receive callbacks whenever the audio route changes or AirPlay connects or disconnects.

Interface

export interface PlayerAudioRouteListener {
onAudiorouteChanged(fromPort: string, toPort: string): void;
onAirplayConnected(isAirplayConnected: boolean): void;
}

Note: Both onAudiorouteChanged and onAirplayConnected are fired from the same underlying native delegate method playerAudioRouteDidChange. They both fire together whenever the audio route changes.

Register the Listener

import { Player, PlayerAudioRouteListener } from '@quickplay/rn-qp-nxg-player';

const audioRouteListener: PlayerAudioRouteListener = {
onAudiorouteChanged(fromPort: string, toPort: string): void {
console.log(`Audio route changed: ${fromPort}${toPort}`);
},

async onAirplayConnected(isAirplayConnected: boolean): Promise<void> {
if (isAirplayConnected) {
console.log('AirPlay connected');
// Adjust UI — e.g. show AirPlay active indicator
} else {
console.log('AirPlay disconnected');
}
},
};

// Add the listener after creating the player
player.addListener(audioRouteListener);

// Remove the listener when the player is disposed
player.removeListener(audioRouteListener);

5. Checking AirPlay Status on the Player

Use player.isAirplayActive() to programmatically check whether AirPlay streaming is currently active for a specific player instance. This calls player.isAirplayRouteActive on the native player.

const isActive: boolean = await player.isAirplayActive();
if (isActive) {
// AirPlay is currently streaming for this player
}

6. Checking AirPlay Route Connection (Device Level)

Use deviceInformation.isAirplayRouteConnected() to check whether an AirPlay output route is connected at the device level, independent of any player instance. This checks AVAudioSession.sharedInstance().currentRoute directly.

import { deviceInformation } from '@quickplay/rn-qp-nxg-player';

const isConnected: boolean = await deviceInformation.isAirplayRouteConnected();
if (isConnected) {
// An AirPlay-compatible output route is currently active on the device
}

Difference between isAirplayActive() and isAirplayRouteConnected():

player.isAirplayActive()deviceInformation.isAirplayRouteConnected()
ScopeSpecific player instanceDevice-level audio session
Requires playerYesNo
Use caseCheck if this player is streaming via AirPlayCheck if any AirPlay route is active on device

7. Complete Example

import React, { useEffect, useRef, useState } from 'react';
import { Platform, View } from 'react-native';
import {
QpNxgAirplayView,
QpNxgPlaybackView,
createPlayer,
Player,
PlaybackStateValue,
BufferingStateValue,
SeekingStateValue,
PlayerConfig,
} from '@quickplay/rn-qp-nxg-player';

export function PlayerScreen({ playerConfig }: { playerConfig: PlayerConfig }) {
const playerRef = useRef<Player | null>(null);
const [playerID, setPlayerID] = useState(-1);
const [isAirplayConnected, setIsAirplayConnected] = useState(false);

useEffect((): any => {
const playerListener = {
onStateChanged(
playbackState: PlaybackStateValue,
bufferingState: BufferingStateValue,
seekingState: SeekingStateValue,
): void {
console.log(`State: ${playbackState}`);
},

onAudiorouteChanged(fromPort: string, toPort: string): void {
console.log(`Audio route: ${fromPort}${toPort}`);
},

async onAirplayConnected(connected: boolean): Promise<void> {
setIsAirplayConnected(connected);
},
};

async function preparePlayer() {
const player = await createPlayer(playerConfig);
playerRef.current = player;
setPlayerID(player.getNativeID());
player.addListener(playerListener);
await player.play();
}

preparePlayer();

return async function cleanup() {
const player = playerRef.current;
if (player != null) {
playerRef.current = null;
await player.stop();
player.removeListener(playerListener);
await player.dispose();
}
};
}, [playerConfig]);

return (
<View style={{ flex: 1 }}>
<QpNxgPlaybackView playerID={playerID} style={{ width: '100%', aspectRatio: 16 / 9 }} />

{/* AirPlay button — only needed in custom RN controls mode */}
{Platform.OS === 'ios' && (
<QpNxgAirplayView
style={{ width: 32, height: 32 }}
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
/>
)}
</View>
);
}

Limitations

  • AirPlay is iOS only. QpNxgAirplayView, isAirplayActive(), isAirplayRouteConnected(), and onAirplayConnected are not available on Android or tvOS.
  • SMPTE-TT subtitles (both SMPTE_ID3 and SMPTE_SIDECAR types) are not supported during AirPlay playback.