mirror of
https://github.com/zoriya/react-native-video.git
synced 2025-12-06 07:16:12 +00:00
feat(android): add asset management functions to plugin (#4494)
* feat: manage asset plugins * fix: continue when plugin is not exoplayer plugin * refactor: change functions plugin type * docs: introduce new plugins
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package com.brentvatne.exoplayer
|
||||
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.datasource.DataSource
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import com.brentvatne.common.api.Source
|
||||
import com.brentvatne.react.RNVPlugin
|
||||
|
||||
/**
|
||||
@@ -15,6 +18,32 @@ interface RNVExoplayerPlugin : RNVPlugin {
|
||||
*/
|
||||
fun getDRMManager(): DRMManagerSpec? = null
|
||||
|
||||
/**
|
||||
* Optional function that allows the plugin to override the media data source factory,
|
||||
* which is responsible for loading media data.
|
||||
* @param source The media source being initialized.
|
||||
* @param mediaDataSourceFactory The current default data source factory.
|
||||
* @return A custom [DataSource.Factory] if override is needed, or null to use default.
|
||||
*/
|
||||
fun overrideMediaDataSourceFactory(source: Source, mediaDataSourceFactory: DataSource.Factory): DataSource.Factory? = null
|
||||
|
||||
/**
|
||||
* Optional function that allows the plugin to modify the [MediaItem.Builder]
|
||||
* before the final [MediaItem] is created.
|
||||
* @param source The source from which the media item is being built.
|
||||
* @param mediaItemBuilder The default [MediaItem.Builder] instance.
|
||||
* @return A modified builder instance if override is needed, or null to use original.
|
||||
*/
|
||||
fun overrideMediaItemBuilder(source: Source, mediaItemBuilder: MediaItem.Builder): MediaItem.Builder? = null
|
||||
|
||||
/**
|
||||
* Optional function that allows the plugin to control whether caching should be disabled
|
||||
* for a given video source.
|
||||
* @param source The video source being loaded.
|
||||
* @return true to disable caching, false to keep it enabled.
|
||||
*/
|
||||
fun shouldDisableCache(source: Source): Boolean = false
|
||||
|
||||
/**
|
||||
* Function called when a new player is created
|
||||
* @param id: a random string identifying the player
|
||||
|
||||
@@ -220,6 +220,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
private Runnable mainRunnable;
|
||||
private Runnable pipListenerUnsubscribe;
|
||||
private boolean useCache = false;
|
||||
private boolean disableCache = false;
|
||||
private ControlsConfig controlsConfig = new ControlsConfig();
|
||||
private ArrayList<Integer> rootViewChildrenOriginalVisibility = new ArrayList<Integer>();
|
||||
|
||||
@@ -750,6 +751,8 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
|
||||
private void initializePlayer() {
|
||||
disableCache = ReactNativeVideoManager.Companion.getInstance().shouldDisableCache(source);
|
||||
|
||||
ReactExoplayerView self = this;
|
||||
Activity activity = themedReactContext.getCurrentActivity();
|
||||
// This ensures all props have been settled, to avoid async racing conditions.
|
||||
@@ -853,7 +856,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
.forceEnableMediaCodecAsynchronousQueueing();
|
||||
|
||||
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory);
|
||||
if (useCache) {
|
||||
if (useCache && !disableCache) {
|
||||
mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
|
||||
}
|
||||
|
||||
@@ -1183,7 +1186,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
|
||||
DataSource.Factory dataSourceFactory = mediaDataSourceFactory;
|
||||
|
||||
if (useCache) {
|
||||
if (useCache && !disableCache) {
|
||||
dataSourceFactory = RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true));
|
||||
}
|
||||
|
||||
@@ -1230,7 +1233,15 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
);
|
||||
}
|
||||
|
||||
MediaItem mediaItem = mediaItemBuilder.setStreamKeys(streamKeys).build();
|
||||
mediaItemBuilder.setStreamKeys(streamKeys);
|
||||
|
||||
@Nullable
|
||||
final MediaItem.Builder overridenMediaItemBuilder = ReactNativeVideoManager.Companion.getInstance().overrideMediaItemBuilder(source, mediaItemBuilder);
|
||||
|
||||
MediaItem mediaItem = overridenMediaItemBuilder != null
|
||||
? overridenMediaItemBuilder.build()
|
||||
: mediaItemBuilder.build();
|
||||
|
||||
MediaSource mediaSource = mediaSourceFactory
|
||||
.setDrmSessionManagerProvider(drmProvider)
|
||||
.setLoadErrorHandlingPolicy(
|
||||
@@ -1921,10 +1932,15 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
boolean isSourceEqual = source.isEquals(this.source);
|
||||
hasDrmFailed = false;
|
||||
this.source = source;
|
||||
this.mediaDataSourceFactory =
|
||||
final DataSource.Factory tmpMediaDataSourceFactory =
|
||||
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
|
||||
source.getHeaders());
|
||||
|
||||
@Nullable
|
||||
final DataSource.Factory overriddenMediaDataSourceFactory = ReactNativeVideoManager.Companion.getInstance().overrideMediaDataSourceFactory(source, tmpMediaDataSourceFactory);
|
||||
|
||||
this.mediaDataSourceFactory = Objects.requireNonNullElse(overriddenMediaDataSourceFactory, tmpMediaDataSourceFactory);
|
||||
|
||||
if (source.getCmcdProps() != null) {
|
||||
CMCDConfig cmcdConfig = new CMCDConfig(source.getCmcdProps());
|
||||
CmcdConfiguration.Factory factory = cmcdConfig.toCmcdConfigurationFactory();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.brentvatne.react
|
||||
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.datasource.DataSource
|
||||
import com.brentvatne.common.api.Source
|
||||
import com.brentvatne.common.toolbox.DebugLog
|
||||
import com.brentvatne.exoplayer.DRMManagerSpec
|
||||
import com.brentvatne.exoplayer.RNVExoplayerPlugin
|
||||
@@ -75,6 +78,35 @@ class ReactNativeVideoManager : RNVPlugin {
|
||||
// ----------------------- RNV Exoplayer plugin specific methods -----------------------
|
||||
fun getDRMManager(): DRMManagerSpec? = customDRMManager
|
||||
|
||||
fun overrideMediaDataSourceFactory(source: Source, mediaDataSourceFactory: DataSource.Factory): DataSource.Factory? {
|
||||
for (plugin in pluginList) {
|
||||
if (plugin !is RNVExoplayerPlugin) continue
|
||||
|
||||
val factory = plugin.overrideMediaDataSourceFactory(source, mediaDataSourceFactory)
|
||||
if (factory != null) return factory
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun overrideMediaItemBuilder(source: Source, mediaItemBuilder: MediaItem.Builder): MediaItem.Builder? {
|
||||
for (plugin in pluginList) {
|
||||
if (plugin !is RNVExoplayerPlugin) continue
|
||||
|
||||
val builder = plugin.overrideMediaItemBuilder(source, mediaItemBuilder)
|
||||
if (builder != null) return builder
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun shouldDisableCache(source: Source): Boolean {
|
||||
for (plugin in pluginList) {
|
||||
if (plugin is RNVExoplayerPlugin && plugin.shouldDisableCache(source)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ----------------------- Custom Plugins Helpers -----------------------
|
||||
private fun maybeRegisterExoplayerPlugin(plugin: RNVPlugin) {
|
||||
if (plugin !is RNVExoplayerPlugin) {
|
||||
|
||||
@@ -111,6 +111,65 @@ In the sample implementation, the plugin is registered in the `createNativeModul
|
||||
|
||||
Once registered, your module can track player updates and report analytics data.
|
||||
|
||||
### Extending Core Functionality via Plugins
|
||||
|
||||
In addition to analytics, plugins can also be used to modify or override core behavior of `react-native-video`.
|
||||
|
||||
This allows native modules to deeply integrate with the playback system - for example:
|
||||
- replacing the media source factory,
|
||||
- modifying the media item before playback starts (e.g., injecting stream keys),
|
||||
- disabling caching dynamically per source.
|
||||
|
||||
These capabilities are available through the advanced Android plugin interface: `RNVExoplayerPlugin`.
|
||||
|
||||
> ⚠️ These extension points are optional — if no plugin provides them, the player behaves exactly as it did before.
|
||||
|
||||
---
|
||||
|
||||
#### Plugin Extension Points (Android)
|
||||
|
||||
If your plugin implements `RNVExoplayerPlugin`, you can override the following methods:
|
||||
|
||||
##### 1. `overrideMediaItemBuilder`
|
||||
|
||||
Allows you to modify the `MediaItem.Builder` before it’s used. You can inject stream keys, cache keys, or override URIs.
|
||||
|
||||
```kotlin
|
||||
override fun overrideMediaItemBuilder(
|
||||
source: Source,
|
||||
mediaItemBuilder: MediaItem.Builder
|
||||
): MediaItem.Builder? {
|
||||
// Return modified builder or null to use default
|
||||
}
|
||||
```
|
||||
|
||||
##### 2. `overrideMediaDataSourceFactory`
|
||||
|
||||
Lets you replace the data source used by ExoPlayer. Useful for implementing read-only cache or request interception.
|
||||
|
||||
```kotlin
|
||||
override fun overrideMediaDataSourceFactory(
|
||||
source: Source,
|
||||
mediaDataSourceFactory: DataSource.Factory
|
||||
): DataSource.Factory? {
|
||||
// Return your custom factory or null to use default
|
||||
}
|
||||
```
|
||||
|
||||
##### 3. `shouldDisableCache`
|
||||
|
||||
Enables dynamic disabling of the caching system per source.
|
||||
|
||||
```kotlin
|
||||
override fun shouldDisableCache(source: Source): Boolean {
|
||||
return true // your own logic
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Once implemented, `react-native-video` will automatically invoke these methods for each `<Video />` instance.
|
||||
|
||||
## iOS Implementation
|
||||
|
||||
### 1. Podspec Integration
|
||||
@@ -308,4 +367,4 @@ class CustomVideoPlugin: RNVAVPlayerPlugin {
|
||||
- On Android, the default ExoPlayer DRM implementation will be used
|
||||
4. The DRM manager must handle all DRM-related functionality:
|
||||
- On iOS: key requests, license acquisition, and error handling through AVContentKeySession
|
||||
- On Android: DRM session management and license acquisition through ExoPlayer's DrmSessionManager
|
||||
- On Android: DRM session management and license acquisition through ExoPlayer's DrmSessionManager
|
||||
|
||||
Reference in New Issue
Block a user