Android SDK (Core)
Integrate External Player

Integrating NwPlayer

9min

We allow you to integrate your preferred media player into our SDK using the NwPlayer interface. This approach, introduced in SDK version 4.10.0 or later, is the recommended and future-proof solution for external player integration. Unlike the deprecated ExoPlayer injection method, NwPlayer gives you full control over the player, allowing you to customize its behavior and even use players other than ExoPlayer.

Below, we provide a high-level overview of integrating your custom player using the NwPlayer interface. For detailed implementation steps, refer to this documentation's code examples and explanations.

Key Features of NwPlayer

  • Full Control: You can customize the player's behavior, including playback, buffering, and error handling.
  • Flexibility: You can wrap any player (e.g., ExoPlayer, MediaPlayer, or a custom player) and integrate it into the SDK.
  • Lifecycle Management: You are responsible for handling the player's lifecycle, including methods like resume, pause, seekTo, and release.
  • Caution Required: Since you have complete control, ensure the player responds correctly to the SDK's calls to avoid playback issues.

Implementation with an Example

Below is a simplified example of how to implement the NwPlayer interface using ExoPlayer:

Kotlin


How to Inject NwPlayer to the SDK

Below, we have provided a code snippet to help you understand how to observe the signal and inject your NwPlayer instance. While this example demonstrates one way to achieve this, feel free to adapt it to fit your application’s architecture and requirements.

Here is the provided code snippet:

Kotlin


Let us break it down step by step:

  1. expInitializePlayer: SharedFlow<CompletableDeferred<NwPlayer>>
    • This is a SharedFlow that emits CompletableDeferred<NwPlayer> objects. A SharedFlow is a Kotlin coroutine construct that allows multiple collectors to receive the same data stream. In this case, it signals when it is time to initialize and inject your NwPlayer instance.
    • CompletableDeferred is a special type of Deferred that can be manually completed. It acts as a promise, allowing the SDK to wait for your NwPlayer instance to be ready before proceeding with playback.
  2. .collect { deferred -> ... }
    • The collect function is used to observe the SharedFlow. Whenever a new CompletableDeferred is emitted, the code inside the collect block is executed.
  3. deferred.complete(ExternalNwPlayer(context))
    • You generate your custom NwPlayer instance using the ExternalNwPlayer(context) function (which you must implement). The complete function is then called on the CompletableDeferred object to provide the SDK with your NwPlayer instance.
    • The complete function returns a Boolean indicating the operation’s success.
  4. Success and Error Logging (Optional)
    • If the complete operation is successful, a log message is printed using Timber.d to confirm that the NwPlayer instance was sent successfully.
    • If it fails, an error message is logged using Timber.e to help you debug the issue.

Why Use CompletableDeferred and SharedFlow?

  • CompletableDeferred: This bridges your code and the SDK. It allows the SDK to wait for your NwPlayer instance to be ready before proceeding, ensuring that playback starts smoothly without timing issues.
  • SharedFlow: This creates a reactive stream that multiple collectors can observe. It ensures the SDK can signal your code at the right moments (e.g., during initialization or stream switches) without tightly coupling your implementation to the SDK’s internal logic.
  • If you do not implement the observer, your event will not use your NwPlayer; instead, it will run on our SDK's internal player.
  • Be careful when calling the collect {} function. It should be called only once; otherwise, you may experience abnormal behavior while streaming any event.

Important: Properties Managed by the SDK

When implementing the NwPlayer interface in your custom class (e.g., ExternalNwPlayer), three critical properties should not be assigned or modified directly within your implementation. The SDK manages These properties exclusively, and any attempt to set or modify their values may lead to unexpected behavior or issues.

  1. override var bitrate: Long = 0L
    • Purpose: Represents the current bitrate of the media stream.
    • Managed By: The SDK automatically updates this value based on the media stream’s bandwidth and quality.
    • Caution: Do not assign or modify this value in your custom class. You cannot use lateinit modifier as it is not allowed on properties of primitive types.
  2. override lateinit var playbackObject: PlaybackObject
    • Purpose: Contains metadata, URLs, DRM configurations, and other details about the media to be played.
    • Managed By: The SDK initializes and assigns this object when preparing the player.
    • Caution: Do not assign or modify this object in your custom class.
  3. override lateinit var playerListener: NwPlayerListener
    • Purpose: A listener communicating player events (e.g., buffering, errors, state changes) to the SDK.
    • Managed By: The SDK assigns this listener to handle player events.
    • Caution: Do not assign or modify this listener in your custom class.

Why Should You Avoid Assigning These Properties?

  • Unexpected Behavior: The SDK relies on these properties to manage playback, track bitrate, and handle events. Assigning or modifying them directly can disrupt the SDK’s functionality.
  • Lifecycle Issues: These properties are initialized and managed at specific SDK lifecycle points. Interfering with them can lead to crashes or playback failures.
  • Event Handling: The playerListener is used to communicate critical events to the SDK. Reassigning it can break event propagation and lead to unresponsive playback.

Here is how these properties should be used in your custom class:

Kotlin


In this example, the properties are declared but not assigned or modified within the class. The SDK will handle their initialization and updates.

Always remember that the SDK manages bitrate, playbackObject, and playerListener. Avoid assigning or modifying these properties in your custom class to ensure smooth and error-free playback. If you encounter issues, double-check that these properties are not being interfered with in your implementation.