Skip to main content
The React Native Video plugin system provides a powerful, extensible architecture that allows you to customize video playback behavior, implement DRM protection, modify media sources, and respond to player lifecycle events.

What Are Plugins?

Plugins are modular extensions that hook into the video player’s lifecycle and processing pipeline. They enable you to extend React Native Video functionality without modifying the core library.

Key Capabilities

Source Customization

Modify video sources before playback - add headers, transform URLs, or apply CDN logic

DRM Integration

Implement custom DRM solutions for Widevine, FairPlay, or proprietary systems

Media Factory Override

Customize ExoPlayer components on Android for advanced playback control

Lifecycle Hooks

React to player and view creation/destruction for analytics and resource management

Architecture Overview

The plugin system is built on three core components:

Core Components

ComponentDescription
PluginsRegistrySingleton that manages plugin registration and coordinates interactions
ReactNativeVideoPluginSpecInterface/protocol defining the plugin contract
ReactNativeVideoPluginBase class with default implementations for easy plugin creation

How It Works

  1. Registration: Plugins register with the PluginsRegistry singleton
  2. Coordination: The registry notifies all plugins of lifecycle events
  3. Processing: Plugins process sources and provide DRM managers in registration order
  4. Execution: The video player uses the modified sources and custom components

Plugin Lifecycle

Plugins receive notifications for key events throughout the player lifecycle:
1

Player Creation

onPlayerCreated is called when a new player instance is initialized. Use this to set up player-specific resources or event listeners.
2

View Creation

onVideoViewCreated is called when the video view component is mounted. Configure UI-specific settings here.
3

Source Processing

overrideSource is called before playback starts. Modify headers, URLs, or other source properties.
4

DRM Resolution

getDRMManager is called when DRM-protected content is detected. Return a DRM manager if your plugin handles the DRM type.
5

Cleanup

onPlayerDestroyed and onVideoViewDestroyed are called during teardown. Clean up resources and remove listeners.
All player and view references passed to plugins are weak references to prevent memory leaks. Always check for null before accessing.

Platform Support

Platform Differences

FeatureAndroid (ExoPlayer)iOS (AVFoundation)
Underlying PlayerExoPlayer (Media3)AVFoundation
Media Factory Customization✅ Extensive⚠️ Limited
Cache Control✅ Supported❌ Not Available
Method StyleSynchronousAsync/await
Memory ManagementManual cleanup requiredAutomatic in deinit

Android-Specific Features

Android plugins can override ExoPlayer components:
  • Media Data Source Factory - Custom network handling and caching
  • Media Source Factory - Custom media format support
  • Media Item Builder - Add metadata, subtitles, DRM configuration
  • Cache Control - Per-source caching policies

iOS-Specific Features

iOS plugins work with AVFoundation:
  • Async/await - All plugin methods support asynchronous operations
  • Automatic Cleanup - Plugins auto-unregister in deinit
  • FairPlay DRM - Native support for Apple’s DRM system

Quick Start Example

Android Plugin

import com.twg.video.core.plugins.ReactNativeVideoPlugin
import java.lang.ref.WeakReference

class AnalyticsPlugin : ReactNativeVideoPlugin("Analytics") {
    override fun onPlayerCreated(player: WeakReference<NativeVideoPlayer>) {
        player.get()?.let {
            logEvent("player_created", mapOf("uri" to it.source.uri))
        }
    }
    
    override fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource {
        // Add analytics headers
        source.headers["X-Session-Id"] = generateSessionId()
        return source
    }
    
    override fun onPlayerDestroyed(player: WeakReference<NativeVideoPlayer>) {
        logEvent("player_destroyed")
    }
}

// Auto-registers on instantiation
val analyticsPlugin = AnalyticsPlugin()

iOS Plugin

import ReactNativeVideo
import Foundation

class AnalyticsPlugin: ReactNativeVideoPlugin {
    init() {
        super.init(name: "Analytics")
    }
    
    override func onPlayerCreated(player: Weak<NativeVideoPlayer>) {
        guard let player = player.value else { return }
        logEvent("player_created", ["uri": player.source.uri])
    }
    
    override func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource {
        // Add analytics headers
        var modifiedSource = source
        modifiedSource.headers["X-Session-Id"] = generateSessionId()
        return modifiedSource
    }
    
    override func onPlayerDestroyed(player: Weak<NativeVideoPlayer>) {
        logEvent("player_destroyed")
    }
    
    // Auto-unregisters in deinit
}

// Auto-registers on instantiation
let analyticsPlugin = AnalyticsPlugin()

Plugin Registration

Automatic Registration

The ReactNativeVideoPlugin base class automatically registers your plugin:
// Android - auto-registers in init block
class MyPlugin : ReactNativeVideoPlugin("MyPlugin") { }
val plugin = MyPlugin() // Registered!
// iOS - auto-registers in super.init
class MyPlugin: ReactNativeVideoPlugin {
    init() { super.init(name: "MyPlugin") }
}
let plugin = MyPlugin() // Registered!

Manual Registration

Implement ReactNativeVideoPluginSpec for full control:
// Android
val customPlugin = object : ReactNativeVideoPluginSpec {
    override val id = "custom_plugin_id"
    override val name = "CustomPlugin"
    // Implement required methods...
}
PluginsRegistry.shared.register(customPlugin)
// iOS
class CustomPlugin: ReactNativeVideoPluginSpec {
    let id = "custom_plugin_id"
    let name = "CustomPlugin"
    // Implement required methods...
}
PluginsRegistry.shared.register(plugin: CustomPlugin())

Plugin Ordering

Plugins are processed in registration order. Later plugins can see and modify changes made by earlier plugins.

Source Processing Chain

// If you have three plugins:
val plugin1 = HeaderPlugin()      // Adds auth headers
val plugin2 = CDNPlugin()          // Modifies URL to use CDN
val plugin3 = AnalyticsPlugin()    // Adds tracking params

// Source flows through all plugins:
// Original → HeaderPlugin → CDNPlugin → AnalyticsPlugin → Final Source

DRM Manager Resolution

The first plugin to return a non-null DRM manager wins:
// Plugin order matters for DRM:
val widevinePlugin = WidevinePlugin()  // Returns manager for Widevine
val fairplayPlugin = FairPlayPlugin()  // Returns manager for FairPlay

// If source.drmType == "widevine":
// 1. widevinePlugin.getDRMManager() returns DRMManager ✓
// 2. Search stops - fairplayPlugin never called

Memory Management

Weak References

Plugins receive weak references to prevent retain cycles:
// Android - always check for null
override fun onPlayerCreated(player: WeakReference<NativeVideoPlayer>) {
    player.get()?.let { p ->
        // Safe to use 'p' here
    }
}
// iOS - use optional binding
override func onPlayerCreated(player: Weak<NativeVideoPlayer>) {
    guard let p = player.value else { return }
    // Safe to use 'p' here
}

Plugin Cleanup

// Manual cleanup required
class MyPlugin : ReactNativeVideoPlugin("MyPlugin") {
    fun cleanup() {
        PluginsRegistry.shared.unregister(this)
    }
}

// In your activity/fragment:
override fun onDestroy() {
    plugin.cleanup()
    super.onDestroy()
}

Available Plugins

DRM Plugin

Official DRM plugin supporting Widevine (Android) and FairPlay (iOS/visionOS)

Create Custom Plugin

Learn how to build your own plugins for custom functionality

Use Cases

Common Plugin Scenarios

  • Authentication - Add bearer tokens to video requests
  • CDN Management - Route videos through optimal CDN endpoints
  • Analytics - Track playback events and performance metrics
  • Content Protection - Implement DRM for Widevine, FairPlay, or custom systems
  • Quality Control - Force specific bitrates or resolutions
  • Caching - Implement custom caching strategies
  • A/B Testing - Switch between video sources for experimentation

Best Practices

Each plugin should have a single, well-defined responsibility. Don’t create “god plugins” that do everything.
Always verify weak references aren’t null before accessing. Players and views may be destroyed at any time.
Plugin methods are called during critical paths. Keep processing fast and defer heavy operations.
Don’t throw exceptions from plugin methods unless absolutely necessary. Log errors and fail gracefully.
Always unregister plugins when done (especially on Android). Remove listeners and release resources.
If your plugin depends on specific player configuration or other plugins, document these requirements.

Next Steps

DRM Plugin

Learn about the official DRM plugin

Creating Plugins

Build your own custom plugins

Plugin Interface

Full API reference for plugin methods

Build docs developers (and LLMs) love