Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add plugins management #3909

Merged
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d3fc4fd
perf: ensure we do not provide callback to native if no callback prov…
freeboub May 5, 2024
e1da32d
chore: rework bufferConfig to make it more generic and reduce ReactEx…
freeboub May 6, 2024
df3da43
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 8, 2024
0f33ad1
chore: improve issue template
freeboub May 8, 2024
17f2385
Merge branch 'TheWidlarzGroup:master' into master
freeboub May 10, 2024
1066898
fix(android): avoid video view flickering at playback startup
freeboub May 10, 2024
485f867
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 11, 2024
a5e10a3
Merge branch 'master' of github.com:freeboub/react-native-video into …
freeboub May 11, 2024
9ce1d95
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 15, 2024
fa27db7
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 17, 2024
be681d3
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 21, 2024
2fadb26
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 23, 2024
92df10d
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 24, 2024
aa61388
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 28, 2024
ceafc53
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 30, 2024
d30c5f5
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 30, 2024
f3f8663
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 3, 2024
5f8c461
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 4, 2024
0eba6da
fix: ensure player doesn't start when view is unmounted
freeboub Jun 4, 2024
8693dbc
Fix/ensure view drop stop playback startup (#3875)
freeboub Jun 6, 2024
89c0e96
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 7, 2024
b8a230e
feat: add plugin system management
freeboub Jun 14, 2024
83dab69
Merge branch 'master' into feat/addAnalyticsPlugManagement
freeboub Jun 14, 2024
39de902
Merge branch 'TheWidlarzGroup:master' into master
freeboub Jun 18, 2024
e67808f
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 18, 2024
dbae966
Merge branch 'feat/addAnalyticsPlugManagement' of github.com:freeboub…
freeboub Jun 18, 2024
73c0511
chore: renaming and cleanup
freeboub Jun 21, 2024
0e62c5a
Merge branch 'master' of github.com:freeboub/react-native-video into …
freeboub Jun 21, 2024
9ee8ea0
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 21, 2024
de0f263
chore: fix linter
freeboub Jun 21, 2024
d2b0715
chore: fix doc
freeboub Jun 22, 2024
cf3402c
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
import com.brentvatne.common.toolbox.DebugLog;
import com.brentvatne.react.BuildConfig;
import com.brentvatne.react.R;
import com.brentvatne.react.ReactNativeVideoManager;
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
import com.brentvatne.receiver.BecomingNoisyListener;
import com.facebook.react.bridge.LifecycleEventListener;
Expand Down Expand Up @@ -258,6 +259,9 @@ public class ReactExoplayerView extends FrameLayout implements
private long lastDuration = -1;

private boolean viewHasDropped = false;

private String instanceId = String.valueOf(UUID.randomUUID());

private void updateProgress() {
if (player != null) {
if (playerControlView != null && isPlayingAd() && controls) {
Expand Down Expand Up @@ -750,6 +754,7 @@ private void initializePlayerCore(ReactExoplayerView self) {
.setLoadControl(loadControl)
.setMediaSourceFactory(mediaSourceFactory)
.build();
ReactNativeVideoManager.Companion.getInstance().onInstanceCreated(instanceId, player);
refreshDebugState();
player.addListener(self);
player.setVolume(muted ? 0.f : audioVolume * 1);
Expand Down Expand Up @@ -1144,6 +1149,7 @@ private void releasePlayer() {
player.removeListener(this);
trackSelector = null;

ReactNativeVideoManager.Companion.getInstance().onInstanceRemoved(instanceId, player);
player = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.brentvatne.common.react.VideoEventEmitter;
import com.brentvatne.common.toolbox.DebugLog;
import com.brentvatne.common.toolbox.ReactBridgeUtils;
import com.brentvatne.react.ReactNativeVideoManager;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
Expand Down Expand Up @@ -97,12 +98,14 @@ public String getName() {
@NonNull
@Override
protected ReactExoplayerView createViewInstance(@NonNull ThemedReactContext themedReactContext) {
ReactNativeVideoManager.Companion.getInstance().registerView(this);
return new ReactExoplayerView(themedReactContext, config);
}

@Override
public void onDropViewInstance(ReactExoplayerView view) {
view.cleanUpResources();
ReactNativeVideoManager.Companion.getInstance().unregisterView(this);
}

@Override
Expand Down
22 changes: 22 additions & 0 deletions android/src/main/java/com/brentvatne/react/RNVAnalyticsPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.brentvatne.react

/**
* Analytics plugin interface definition
*/
interface RNVAnalyticsPlugin {
/**
* Function called when a new player is created
* @param id: a random string identifying the player
* @param player: the instantiated player reference
*/
fun onInstanceCreated(id: String, player: Any)

/**
* Function called when a player should be destroyed
* when this callback is called, the analytics plugin shall free all
* resources and release all reference to Player object
* @param id: a random string identifying the player
* @param player: the player to release
*/
fun onInstanceRemoved(id: String, player: Any)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.brentvatne.react

import com.brentvatne.common.toolbox.DebugLog
import com.brentvatne.exoplayer.ReactExoplayerViewManager

/**
* ReactNativeVideoManager is a singleton class which allows to manipulate / the global state of the app
* It handles the list of <Video view instanced and registration of analytics plugins
*/
class ReactNativeVideoManager : RNVAnalyticsPlugin {
companion object {
private const val TAG = "ReactNativeVideoManager"

@Volatile
private var instance: ReactNativeVideoManager? = null

/**
* Singleton accessor
*/
fun getInstance(): ReactNativeVideoManager =
instance ?: synchronized(this) {
instance ?: ReactNativeVideoManager().also { instance = it }
}
}

private var instanceList: ArrayList<ReactExoplayerViewManager> = ArrayList()
private var analyticsPluginList: ArrayList<RNVAnalyticsPlugin> = ArrayList()

/**
* register a new ReactExoplayerViewManager in the managed list
*/
fun registerView(newInstance: ReactExoplayerViewManager): () -> Boolean =
{
if (instanceList.size > 2) {
DebugLog.d(TAG, "multiple Video displayed ?")
}
instanceList.add(newInstance)
}

/**
* unregister existing ReactExoplayerViewManager in the managed list
*/
fun unregisterView(newInstance: ReactExoplayerViewManager): () -> Boolean =
{
instanceList.remove(newInstance)
}

/**
* register a new analytics plugin in the managed list
*/
fun registerAnalyticsPlugin(plugin: RNVAnalyticsPlugin) {
analyticsPluginList.add(plugin)
return
}

/**
* unregister a analytics plugin from the managed list
*/
fun unregisterAnalyticsPlugin(plugin: RNVAnalyticsPlugin) {
analyticsPluginList.remove(plugin)
return
}

override fun onInstanceCreated(id: String, player: Any) {
analyticsPluginList.forEach { it.onInstanceCreated(id, player) }
}

override fun onInstanceRemoved(id: String, player: Any) {
analyticsPluginList.forEach { it.onInstanceRemoved(id, player) }
}
}
3 changes: 2 additions & 1 deletion docs/pages/other/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"caching": "Caching",
"misc": "Misc",
"debug": "Debugging",
"new-arch": "New Architecture"
"new-arch": "New Architecture",
"analytics": "Analytics plugin"
}
125 changes: 125 additions & 0 deletions docs/pages/other/analytics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Analytics plugin (experimental)

Since Version 6.3.0, it is possible to create plugins for analytics management (and maybe much more).
A sample plugin is available in the repository in: example/react-native-video-plugin-sample. (important FIXME, put sample link)

## Concept

Most of the analytics system which tracks player information (bitrate, errors, ...) can be integrated directly with Exoplayer or AVPlayer handles.

This plugin system allows none intrusive integration of analytics in the react-native-package. It shall be done in native language (kotlin/swift).

The idea behind this system is to be able to plug an analytics package to react native video without doing any code change (ideally).

Following documentation will show on how to create a new plugin for react native video

## Warning and consideration
This is an experiental API, it is subject to change. The api with player is very simple but should be flexible enough to implement analytics system. If you need some metadata, you should implement setter in the new package you are creating.

As api is flexible, it makes possible to missuse the system. It is necessary to consider the player handle as read-only. If you modify player behavior, we cannot garanty the good behavior of react-native-video package.

## General

First you need to create a new react native package:
````shell
npx create-react-native-library@latest react-native-video-custom-analytics
````

Both android and iOS implementation expose an interface `RNVAnalyticsPlugin`.
Your `react-native-video-custom-analytics` shall implement this interface and register itself as a plugin for react native video.

## Android
There is no special requierement for gradle file.
You need two mandatory action to be able to receive player handle

### 1/ Create the plugin

First you should instanciate a class which extends `RNVAnalyticsPlugin`.

The proposed integration implement `RNVAnalyticsPlugin` directly inside the Module file (`VideoPluginSampleModule`).

The `RNVAnalyticsPlugin` interface only defines 2 functions, see description here under.

```kotlin
/**
* Function called when a new player is created
* @param id: a random string identifying the player
* @param player: the instantiated player reference
*/
fun onInstanceCreated(id: String, player: Any)
/**
* Function called when a player should be destroyed
* when this callback is called, the analytics plugin shall free all
* resources and release all reference to Player object
* @param id: a random string identifying the player
* @param player: the player to release
*/
fun onInstanceRemoved(id: String, player: Any)
````

### 2/ register the plugin

To register this allocated class in the main react native video package you should call following function:

```kotlin
ReactNativeVideoManager.getInstance().registerAnalyticsPlugin(plugin)
```
The proposed integration register the instanciated class in `createNativeModules` entry point.

Your native module can now track Player updates directly from Player reference and report to backend.

## ios

### 1/ podspec integration

Your new module shall be able to access to react-native-video package, then we must declare it as a dependency of the new module you are creating.

```podfile
s.dependency "react-native-video"
````

### 2/ Create the plugin

First you should instanciate a class which extends `RNVAnalyticsPlugin`.

The proposed integration implement `RNVAnalyticsPlugin` directly inside the entry point of the module file (`VideoPluginSample`).

The `RNVAnalyticsPlugin` interface only defines 2 functions, see description here under.

```swift
/**
* Function called when a new player is created
* @param player: the instantiated player reference
*/
func onInstanceCreated(player: Any)
/**
* Function called when a player should be destroyed
* when this callback is called, the analytics plugin shall free all
* resources and release all reference to Player object
* @param player: the player to release
*/
func onInstanceRemoved(player: Any)
```

### 3/ Register the plugin

To register this allocated class in the main react native video package you should register it by calling this function:

```swift
ReactNativeVideoManager.shared.registerAnalyticsPlugin(plugin: plugin)
```

The proposed integration register the instanciated class in file `VideoPluginSample` in the init function:

```swift
import react_native_video

...

override init() {
super.init()
ReactNativeVideoManager.shared.registerAnalyticsPlugin(plugin: self)
}
```

Your native module can now track Player updates directly from Player reference and report to backend.
1 change: 1 addition & 0 deletions examples/basic/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ dependencies {
}

implementation project(':react-native-video')
implementation project(':react-native-video-plugin-sample')

constraints {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import com.brentvatne.react.ReactVideoPackage
import com.videopluginsample.VideoPluginSamplePackage

class MainApplication : Application(), ReactApplication {

Expand All @@ -21,6 +22,7 @@ class MainApplication : Application(), ReactApplication {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add(ReactVideoPackage())
add(VideoPluginSamplePackage())
}

override fun getJSMainModuleName(): String = "src/index"
Expand Down
3 changes: 3 additions & 0 deletions examples/basic/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ rootProject.name = 'videoplayer'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'

include ':react-native-video-plugin-sample'
project (':react-native-video-plugin-sample').projectDir = new File(rootProject.projectDir, '../../react-native-video-plugin-sample/android')

include ':react-native-video'
project (':react-native-video').projectDir = new File(rootProject.projectDir, '../../../android')

Expand Down
1 change: 1 addition & 0 deletions examples/basic/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target 'videoplayer' do
)

pod 'react-native-video', path: '../../..'
pod 'react-native-video-plugin-sample', path: '../../react-native-video-plugin-sample'

target 'videoplayerTests' do
inherit! :complete
Expand Down
Loading
Loading