Handling common player errors
This document provides solutions and best practices for addressing common errors encountered during video playback using the rn-qp-nxg-player library. These errors include issues related to live streaming, DRM, session expiry, and more.
BehindLiveWindowException (Error: 40020d)
The Player throws a BehindLiveWindowException when the playback strays so far away from live edge that it falls behind the available live window. The playback can fall behind because of continuous buffering or pausing of a live stream. This exception can be prevented by turning on the live monitor via PlaybackProperties and passing a LiveStreamMonitorConfiguration, if needed.
Live stream monitoring is disabled by default.
| Property Name | Type | Default Value | Description |
|---|---|---|---|
| liveStreamMonitorConfiguration | LiveStreamMonitorConfiguration | monitoringTimeIntervalMs = 10000 liveEdgePositionThreshold = 20 currentWindowBufferedPositionThreshold = 40 | The live stream configuration to be employed. |
| monitorLiveStream | Boolean | false | Represents whether to monitor live stream position or not. |
let liveStreamMonitorConfig: LiveStreamMonitorConfiguration = {
monitoringTimeIntervalMs: 3000,
currentWindowBufferedPositionThreshold: 40,
};
DECODER_INIT_FAILURE (Error: 400208)
One possible cause of this error is when the app attempts to play DRM streams exceeding the device's supported limit. To address this, a new parameter, shouldCleanupPreviousPlayers, has been added to PlayerConfig. This ensures that any existing player is stopped before creating a new one.
let playerConfig: PlayerConfig = {
...
shouldCleanupPreviousPlayers: true
...
}
let player = await createPlayer(playerConfig)
Handling player errors
Here is a step-by-step guide to handling playback errors.
The PlatformError interface provides structured error information:
export interface PlatformError {
errorCode: number;
errorDescription: string;
contextDescription?: string;
httpStatusCode?: number;
internalError?: PlatformError;
readonly componentErrorCode: number;
readonly hexErrorCode: string;
doesBelongToCategory: (category: number) => boolean;
toString: () => string;
}
When the player encounters a failure, it reports the error using the onError callback:
onError(error: PlatformError): void {
// Handle error
},
SSAI Session Expiry
In SSAI playbacks, ads are added dynamically from the server, so the server needs to keep a session for each playback to handle the ads properly. The server usually cleans up idle sessions after a while.
In iOS apps, playback can pause for a long time, causing sessions to be removed from the server. When users return to the app, errors can occur because the player tries to resume a session that no longer exists.
To prevent this, apps must close the playback session before becoming idle. Relying solely on app background/foreground state transitions is insufficient because features like Picture-in-Picture and AirPlay require special handling.
Background
App goes to background
- Open the iOS/tvOS app.
- Play any SSAI stream.
- Pause the playback.
- Put the app in the background.
- Open the app after the SSAI session keep-alive time has elapsed, causing the player to throw a session expiry error.
Solution
Deinitialize the player instance when the app is backgrounded. This prevents the player from encountering errors when it becomes active after a session expiry.
Implementation
import { AppState, AppStateStatus } from 'react-native';
import React, { useRef, useEffect } from 'react';
const appState = useRef<AppStateStatus>(AppState.currentState);
useEffect(() => {
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
if (nextAppState === "background") {
appState.current = nextAppState;
// TODO: Deinitialize the player
}
};
const subscription = AppState.addEventListener("change", handleAppStateChange);
return () => {
subscription.remove();
};
}, []);
Picture-in-Picture
Scenario
- Open the iOS app.
- Play any SSAI-enabled stream.
- Put the app in the background; the stream continues playing in a PiP window.
- Close the PiP window.
- Open the app after the SSAI session elapses, causing the player to throw a session expiry error.
Solution
Deinitialize the player instance when the PiP window is closed. This prevents the player from encountering errors when it becomes active after a session expiry.
Implementation
let playerListener = {
async onPictureInPictureStopped(): Promise<void> {
if (appState.current === "background") {
// TODO: Deinitialize the player
}
},
};
AirPlay
Scenario
- Open the iOS app.
- Play any SSAI stream.
- AirPlay the stream to an external device.
- Put the iOS app in the background and close the playback on the external device using a remote.
- Open the app after the SSAI session keep-alive time has elapsed, causing the player to throw a session expiry error.
Solution
Deinitialize the player instance when the app is backgrounded and the AirPlay device stops playback. This prevents the player from encountering errors when it becomes active after a session expiry.
Implementation
let playerListener = {
async onAudiorouteChanged(routeChange: string, fromPort: string, toPort: string): Promise<void> {
if (fromPort === "AirPlay" && toPort !== "AirPlay" && appState.current === "background") {
console.log("AirPlay stopped in background, stopping player...");
// TODO: Deinitialize the player
}
},
};
HTTP Failures
Playback may pause for an extended period, causing sessions to be removed from the server. When users return, errors can occur as the player tries to resume the previous session. Handle HTTP status codes in the onError callback to respond appropriately:
onError(error: PlatformError): void {
if (error.httpStatusCode) {
switch (error.httpStatusCode) {
case 410:
// Session gone — deinitialize and prompt user to restart playback
break;
}
}
}
SSAI session expiry varies for different SSAI vendors and environments. Picture-in-Picture and AirPlay are not applicable on tvOS.