Architecture
The core of the library relies on PRESTOplay SDK for iOS and Android. By delegating video playback to the native layers, the library ensures optimal performance and makes full use of platform-specific capabilities. These native implementations handle all playback, buffering, and rendering operations, providing the foundation for reliable and efficient video streaming.
To integrate these native components with React Native, a proxy layer PRESTOplay SDK for React Native is employed. This proxy serves as a bridge between the JavaScript environment and the native players, abstracting the complexities of cross-layer communication and exposing a unified API for developers. The proxy approach ensures that video playback operations remain performant, avoiding common bottlenecks in hybrid frameworks.
Within the JavaScript layer, a player proxy takes responsibility for managing the interaction between JavaScript and the native player. This proxy plays a critical role in facilitating communication and data model synchronization. Native players broadcast all model changes through the event loop (marked as 1 in the diagram). The player proxy listens to these events, caches the updated values in its internal model, and re-triggers the events to all listeners in the JavaScript environment. This event-driven mechanism ensures that the model changes are propagated efficiently and without unnecessary delays.
The player proxy distinguishes between two types of interactions: synchronous getters and asynchronous setters. Getters in the player proxy, such as getDuration, getState, and isLive, are designed to return cached values immediately. By using cached values, the proxy avoids querying the native player or invoking the React Native Bridge, which would introduce latency. This approach improves performance by reducing overhead and ensures that UI rendering remains smooth and responsive.
In contrast, setters, such as pause, setVideoRendition, and setVolume, are asynchronous operations. These functions communicate with the native player through the React Native Bridge to modify the player’s state (marked as 2 in the diagram). Because these operations involve bridging between JavaScript and native layers, they are considered heavier operations. This design decision ensures that getters remain lightweight and suitable for frequent use, while setters are reserved for state-changing commands.
A key feature of the library is the decoupling of the player controller from the video renderer. This separation allows the library to support advanced playback scenarios, such as prefetching video streams in the background or quickly switching between different players while using fewer resources. For example, a user could seamlessly switch between a main player and an ad player, or prefetch a new video stream in the background when a user is likely to select a particular stream. This decoupling adds flexibility and allows developers to implement sophisticated playback features without sacrificing performance.