Skip to main content

Platform Player

Platform Player

This page covers additional player features and configurations for the Quickplay React Native Player SDK.


Subtitles and Closed Captions

Subtitles

To enable subtitles, set the TrackVariantTypeValue to 'TEXT':

// Retrieve the available subtitle tracks
let subtitleTracks = await player.getAvailableTrackVariants('TEXT');
// Send the selected subtitle track to the player
let selectedTrack = subtitleTracks[selectedIndex];
await player.setPreferredTrackVariant('TEXT', selectedTrack);
// If the user disables the subtitles
await player.setPreferredTrackVariant('TEXT', undefined);

// Retrieve the selected track for given variantType
let selectedSubtitleTrack = await player.getSelectedTrack('TEXT');

Closed Captions

To enable closed captions, set the TrackVariantTypeValue to 'CLOSED CAPTION':

// Retrieve the available closed caption tracks
let closedCaptions = await player.getAvailableTrackVariants('CLOSED CAPTION');
if (closedCaptions.length > 0) {
// show CC button
}
// Select a closed caption track
let selectedCaption = closedCaptions[0];
await player.setPreferredTrackVariant('CLOSED CAPTION', selectedCaption);
// If the user disables the closed captions
await player.setPreferredTrackVariant('CLOSED CAPTION', undefined);

// Retrieve the selected track
let selectedSubtitleTrack = await player.getSelectedTrack('CLOSED CAPTION');

Note: If the device's accessibility settings are enabled to automatically show subtitles/CC, the device will pre-select the track. When playback starts, check which TEXT and CLOSED CAPTION tracks are selected and update the UI accordingly.

Subtitle Positioning

Adjust the subtitle position between 0 (top of screen) and 100 (bottom of screen):

await player.setSubtitlePosition(subtitlePosition: SubtitlePosition);

Note: Typically used to raise the subtitle position above controls when they become visible.

Preferred Subtitle / Audio Language

To select subtitle or audio language at playback start, use preferredSubtitleLanguage and preferredAudioLanguage in PlayerConfig. Supports ISO 639-1 (two-letter) and ISO 639-2/3 (three-letter) language codes:

let playerConfig: PlayerConfig = {
preferredSubtitleLanguage: 'fr',
preferredAudioLanguage: 'en',
};
let player = await createPlayer(playerConfig);

Track Availability Callback

The trackAvailabilityChanged listener notifies applications when new tracks become available during playback:

async onTrackAvailabilityChanged(): Promise<void> {}

SMPTE-TT Subtitles

SMPTE-TT is a bitmap/image-based subtitle format embedded in the stream as XML via ID3 timed metadata, or delivered as a sidecar XML file.

Enable SMPTE-TT Subtitle Rendering

Note: This is a resource-intensive operation — enable it only for channels that support SMPTE-TT subtitles.

XML via ID3 Event

const playerPreference: PlayerPreference = {
enableAdditionalSubtitle: enableSubtitleMediaTrackType ? ['SMPTE-TT'] : undefined,
};
player.setPlayerPreference(playerPreference);

XML via SideCar Load

const textTracks = [
{
endpoint: 'https://example.com/subtitles_eng.ttml',
languageCode: 'en',
},
{
endpoint: 'https://example.com/subtitles_chi.ttml',
languageCode: 'chi',
},
];
let playerConfig: PlayerConfig = {
smpteTimedTextTracks: textTracks,
};
let player = await createPlayer(playerConfig);

Subtitle Selection Behavior

PreferenceAvailable TracksDisplayed
'SMPTE_ID3''SMPTE_ID3''SMPTE_ID3'
'SMPTE_SIDECAR''SMPTE_SIDECAR''SMPTE_SIDECAR'
'SMPTE_SIDECAR, SMPTE_ID3''SMPTE_SIDECAR, SMPTE_ID3''SMPTE_ID3'
undefinedNative supportedNative supported
undefined / No preference'SMPTE_ID3'No tracks display

Limitations

  • Not supported on tvOS.
  • Not supported during AirPlay or Picture-in-Picture playback.
  • SMPTE-TT tracks become available only after playback starts.
  • Multi-language SMPTE-TT subtitles are not supported on iOS 16 and below (ID3 timed metadata not reported).

Audio Description Support

Audio description tracks provide spoken narration of visual elements for accessibility.

Detecting Audio Description Tracks

const audioTracks = await player.getAvailableTrackVariants('AUDIO');

audioTracks.map((track: TrackVariantInfo, index: number) => {
if (track.role?.includes('DESCRIBES_VIDEO')) {
console.log(`Audio description track found: ${track.displayName}`);
}
});

Audio Track Role Types

RoleDescription
MAINPrimary audio track
DESCRIBES_VIDEOAudio description track for accessibility
NONENo specific role assigned

Selecting Audio Description Tracks

const audioTracks = await player.getAvailableTrackVariants('AUDIO');
const audioDescriptionTrack = audioTracks.find(track =>
track.role?.includes('DESCRIBES_VIDEO')
);

if (audioDescriptionTrack) {
await player.selectTrackVariant(audioDescriptionTrack);
}

Low Latency

iOS

Set preferredInitialForwardBufferDuration and preferredForwardBufferDurationAfterRebuffer for minimal delay:

let playerPreference: PlayerPreference = {
preferredInitialForwardBufferDuration: 1,
preferredForwardBufferDurationAfterRebuffer: 1,
};
player.setPlayerPreference(playerPreference);

Android

let playbackProperties: PlaybackProperties = {
preferredMinBufferDurationMs: 3000,
preferredMaxBufferDurationMs: 15000,
preferredInitialBufferDurationMs: 1500,
preferredBufferDurationAfterRebufferMs: 3000,
preferredTargetLiveOffsetIncrementOnRebufferMs: 0,
};
let playerConfig: PlayerConfig = {
playbackProperties: playbackProperties,
};
let player = await createPlayer(playerConfig);

Dolby Support

By default on Android, playback uses Dolby Atmos if both the device and content support it.

To switch between audio codecs:

// Switch to Dolby Digital Plus
player.setPreferredMimeType('AUDIO', ['audio/eac3']);

// Switch to stereo
player.setPreferredMimeType('AUDIO', ['audio/mp4']);

// Switch to Dolby Atmos
player.setPreferredMimeType('AUDIO', ['audio/eac3-joc']);

Security

Device Security Status

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

const { isSecure, failureReason } = await deviceInformation.securityStatus();
PropertyTypeDescription
isSecurebooleanWhether the device is secure
failureReasonstring?Reason why the device is insecure (if applicable)

Protect Playback from Insecure Devices

let playerConfig: PlayerConfig = {
securityConfig: {
allowPlaybackOnInsecureDevice: false,
},
};

let player = await createPlayer(playerConfig);

Enable Additional Security Checks

let playerConfig: PlayerConfig = {
securityConfig: {
allowPlaybackOnInsecureDevice: false,
additionalSecurityChecksAndroid: [
SecurityChecksAndroid.CHECK_FOR_ROOT_APPS,
SecurityChecksAndroid.CHECK_FOR_ROOT_CLOCK_APPS,
],
additionalSecurityChecksIOS: [
SecurityChecksIOS.PROXY_CHECK,
SecurityChecksIOS.INTEGRITY_CHECK,
],
bundleIdIOS: 'com.org.app', // Required for INTEGRITY_CHECK
},
};

Android Security Checks

ConstantValueDescription
CHECK_FOR_ROOT_APPS1Checks for root management apps
CHECK_FOR_DANGEROUS_APPS2Checks for dangerous apps
CHECK_FOR_ROOT_CLOCK_APPS3Checks for root clock apps
CHECK_FOR_WRITABLE_PATHS4Checks if restricted paths are writable
CHECK_FOR_MAGISK_BINARY5Checks for Magisk binary
CHECK_FOR_DANGEROUS_PROPS6Checks for dangerous system properties
CHECK_FOR_ROOT_NATIVE7Native root checks

iOS Security Checks

ConstantValueDescription
DEBUGGER_CHECK1Detects if the app is being debugged
EMULATOR_CHECK2Detects if running on an emulator
PROXY_CHECK3Detects HTTP proxy in iOS Settings
INTEGRITY_CHECK4Detects if the app has been tampered with

Advanced Security Check (iOS only)

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

const additionalChecks = [
SecurityChecksIOS.DEBUGGER_CHECK,
SecurityChecksIOS.EMULATOR_CHECK,
];

const result = await deviceInformation.securityStatusWithChecks(additionalChecks);
console.log('Is Secure:', result.isSecure);
console.log('Failure Reason:', result.failureReason);

License Management

Support for License Refresh

License Refresh is needed when configured with CSAI. The player auto-detects LA_URL expiry and renews it before acquiring the license.

Note: Works on Android only; integration does not need to change for iOS.

  1. Initialize contentAuthorizer with licenseRefreshEndPointURL:
const contentAuthConfig = {
licenseRefreshEndPointURL: 'https://license-refresh.api.<tenant>.firstlight.ai',
};

await contentAuthorizer.initWithConfig(contentAuthConfig);
  1. Pass mandatory variables in playerConfig:
let contentAuthToken = await contentAuthorizer.authorizePlayback(platformAsset);

let playerConfig: PlayerConfig = {
contentID: string,
drmLicenseURL: contentAuthToken.licenseURL,
drmLicenseRefreshToken: contentAuthToken.licenseRefreshToken,
};

let player = await createPlayer(playerConfig);

Playback Customization

Optimizing OAuth Token Refresh

By default, the SDK no longer disposes the auth token when the app goes to background. To opt back into the old behavior:

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

let disposeAuthTokenOnBackground = true; // Android only

await platformAuthorizer.initWithConfig(config, disposeAuthTokenOnBackground);

Playback Auto Recovery

preferredMaxPlaybackRecoveryRetryCount configures the maximum recovery attempts (default: 3).

Auto Recovery Callbacks

onPlaybackRecoveryStatus(playbackRecoveryStatus: PlaybackRecoveryStatus): void {
const {
error,
recoveryStatus,
currentRetryAttempt,
totalRetryAttempts,
recoveryAttemptsDurationMs,
reportingEventPayload, // Must be reported for analytics
} = playbackRecoveryStatus;
}

PlaybackRecoveryStatus Properties

PropertyTypeDescription
errorPlatformErrorError encountered during recovery
recoveryStatusRecoveryStatusSuccess or Failure
currentRetryAttemptnumberNumber of recovery attempts made
totalRetryAttemptsnumberTotal configured retry attempts
recoveryAttemptsDurationMsnumber[]Duration (ms) for each attempt
reportingEventPayloadstringJSON payload for analytics reporting

Setting Recoverable Errors

const recoverablePlayerErrors: Array<string> = [
'400208', // MEDIA_PLAYBACK_FAILURE_ERROR
'40020D', // INVALID_LIVE_MEDIA_SEGMENT_ERROR
'40020E', // NETWORK_CONNECTION_FAILURE_ERROR
'40020F', // NETWORK_END_POINT_UNREACHABLE_ERROR
];

let playerConfig: PlayerConfig = {
playbackProperties: { recoverablePlayerErrors },
};
let player = await createPlayer(playerConfig);

Setting Initial Network Bandwidth

let playbackProperty = {
preferredInitialNetworkBandwidth: number,
preferredMinDurationForQualityIncreaseMs: number,
preferredMaxDurationForQualityDecreaseMs: number,
preferredMinDurationToRetainAfterDiscardMs: number,
preferredBandwidthFraction: number,
preferredMaxPlaybackRecoveryRetryCount: number,
};

let playerConfig: PlayerConfig = {
playbackProperties: playbackProperty,
};
let player = await createPlayer(playerConfig);

Note: These configurations work only on Android; integration does not need to change for iOS.


Network Stack

Set a preferred NetworkStack for the player (Android only):

let playerConfig: PlayerConfig = {
networkStack: 'CRONET_GPS',
};
let player = await createPlayer(playerConfig);
Enum ValueProtocolsAPK size impactNotes
BUILT_IN (default)HTTP (varies by device)NoneImplementation varies by device
OK_HTTPHTTP, HTTP/2Small (<1 MB)Requires Kotlin runtime
CRONET_GPSHTTP, HTTP/2, HTTP/3 (QUIC)Small (<100 KB)Requires Google Play Services

Required dependencies:

// OK_HTTP
implementation 'com.google.android.exoplayer:extension-okhttp:2.X.X'

// CRONET_GPS
implementation 'com.google.android.exoplayer:extension-cronet:2.X.X'

Media Metadata Support

const mediaMetadataRecord: Record<AndroidMediaMetadataKeys, string> = {
TITLE: '<Title of the Asset>',
DISPLAY_TITLE: '<Display Title of the Asset>',
// ...
};

let playerConfig: PlayerConfig = {
mediaMetadata: mediaMetadataRecord,
};
let player = await createPlayer(playerConfig);

Thumbnail Extended Configuration

Note: Android only.

ParameterTypeDefaultDescription
isPrefetchRequiredBooleantruePre-loads sprites and caches thumbnails
shouldDeleteFromDiskOnExitBooleantrueDeletes cached thumbnails when playback exits
maxConcurrentThumbnailDownloadsIntegerAvailable processorsMax concurrent thumbnail downloads
const thumbnailExtendedConfig = {
isPrefetchRequired: true,
shouldDeleteFromDiskOnExit: true,
maxConcurrentThumbnailDownloads: 2,
};

let playerConfig: PlayerConfig = {
thumbnailExtendedConfig,
};
let player = await createPlayer(playerConfig);

Device Information & Capabilities

Audio / Video Codecs

Note: Android only — returns undefined on iOS.

let audioCodecs = await deviceInformation.supportedAudioCodecs();
let videoCodecs = await deviceInformation.supportedVideoCodecs();

Device Capability

let deviceCapability = await deviceInformation.getDeviceCapability();
// 'HIGH_END' | 'MID_RANGE' | 'LOW_END'

Maximum Secure Decoders

let maxDecoderInstances = await deviceInformation.getMaxSecuredDecoderInstances();
let hevcCodecName = maxDecoderInstances['video/hevc'].name;
let avcCodecName = maxDecoderInstances['video/avc'].name;

CPU Core Count

const cpuCoreCount = await deviceInformation.getTotalCPUCoreCount();

Supported GPU Family (iOS)

const supportedGPUFamilies = await deviceInformation.getAllSupportedGPUFamily();

Total RAM

const totalRam = await deviceInformation.getTotalRamInGB();

Universal Search (iOS / tvOS)

Manual Reporting Using MPNowPlayingInfoCenter

  1. Remote commands are registered automatically when the player is created and unregistered when it is deinitialized.

  2. Set constant metadata after player creation:

const playingInfo: PlayingInfo = {
title: 'Interstellar',
contentIdentifier: '550e8400-e29b-41d4-a716-446655440000',
};
setNowPlayingInfo(playingInfo);
  1. Enable automatic position/rate updates:
const playerPreference: PlayerPreference = {
assignRemoteActions: true,
};
player.setPlayerPreference(playerPreference);

tvOS Compatibility

Some older Apple TV HD models do not recognize metadata from MPNowPlayingSession. Use the shared MPNowPlayingInfoCenter instead:

const playerPreference: PlayerPreference = {
useSharedNowPlayingInfoCenter: true,
};
player.setPlayerPreference(playerPreference);

QPShorts Integration

import { ShortsPlayer, Video } from '@quickplay/rn-qp-nxg-player';

const videos: Video[] = [
{
id: '8f1d0955-4423-4856-bc59-8a378abd7db9',
title: '',
description: 'Sample short',
contentType: 'video',
contentUrls: [
'https://example.com/video_1024.MP4',
'https://example.com/video_512.MP4',
],
likes: 1,
shares: 4,
views: 170,
comments: 23,
user: {
id: '16fabb52-3b32-4eb4-ad30-c16c6b8c1c08',
userName: 'sampleUser',
fullName: 'Sample User',
followers: 0,
},
},
];

const shorts = await ShortsPlayer.play(videos);

Syndication SSO

  1. Initialize contentAuthorizer with syndicationSSOEndPointURL:
import { ContentAuthConfig, contentAuthorizer } from '@quickplay/rn-qp-nxg-player';

const contentAuthConfig = {
syndicationSSOEndPointURL: 'https://sso.example.com',
};

await contentAuthorizer.initWithConfig(contentAuthConfig);
  1. Call authorizePartnerPlayback with a PartnerPlatformAsset (contentId, contentTypeId, catalogType are mandatory):
let partnerAuthorizationData = await contentAuthorizer.authorizePartnerPlayback(platformAsset);

The response contains:

{
contentId: string; // same content ID as request
deeplinkUrl: string; // playback deep link
deeplinkToken: string; // authentication token
}