feat: add plugins management (#3909)

This commit is contained in:
Olivier Bouillet 2024-06-25 08:55:32 +02:00 committed by GitHub
parent 3cfb96adb9
commit 91d27a6009
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 828 additions and 140 deletions

View File

@ -123,6 +123,7 @@ import com.brentvatne.common.react.VideoEventEmitter;
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;
@ -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) {
@ -756,6 +760,7 @@ public class ReactExoplayerView extends FrameLayout implements
.setLoadControl(loadControl)
.setMediaSourceFactory(mediaSourceFactory)
.build();
ReactNativeVideoManager.Companion.getInstance().onInstanceCreated(instanceId, player);
refreshDebugState();
player.addListener(self);
player.setVolume(muted ? 0.f : audioVolume * 1);
@ -1150,6 +1155,7 @@ public class ReactExoplayerView extends FrameLayout implements
player.removeListener(this);
trackSelector = null;
ReactNativeVideoManager.Companion.getInstance().onInstanceRemoved(instanceId, player);
player = null;
}

View File

@ -20,6 +20,7 @@ import com.brentvatne.common.api.SubtitleStyle;
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;
@ -97,12 +98,14 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
@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

View File

@ -0,0 +1,22 @@
package com.brentvatne.react
/**
* Plugin interface definition
*/
interface RNVPlugin {
/**
* 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 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)
}

View File

@ -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 plugins
*/
class ReactNativeVideoManager : RNVPlugin {
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 pluginList: ArrayList<RNVPlugin> = 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 plugin in the managed list
*/
fun registerPlugin(plugin: RNVPlugin) {
pluginList.add(plugin)
return
}
/**
* unregister a plugin from the managed list
*/
fun unregisterPlugin(plugin: RNVPlugin) {
pluginList.remove(plugin)
return
}
override fun onInstanceCreated(id: String, player: Any) {
pluginList.forEach { it.onInstanceCreated(id, player) }
}
override fun onInstanceRemoved(id: String, player: Any) {
pluginList.forEach { it.onInstanceRemoved(id, player) }
}
}

View File

@ -2,5 +2,6 @@
"caching": "Caching",
"misc": "Misc",
"debug": "Debugging",
"new-arch": "New Architecture"
"new-arch": "New Architecture",
"plugin": "Plugin (experimental)"
}

125
docs/pages/other/plugin.md Normal file
View File

@ -0,0 +1,125 @@
# Plugin (experimental)
Since Version 6.4.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 `RNVPlugin`.
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 `RNVPlugin`.
The proposed integration implement `RNVPlugin` directly inside the Module file (`VideoPluginSampleModule`).
The `RNVPlugin` 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 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().registerPlugin(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 `RNVPlugin`.
The proposed integration implement `RNVPlugin` directly inside the entry point of the module file (`VideoPluginSample`).
The `RNVPlugin` 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 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.registerPlugin(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.registerPlugin(plugin: self)
}
```
Your native module can now track Player updates directly from Player reference and report to backend.

View File

@ -137,6 +137,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") {

View File

@ -14,6 +14,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 {
@ -24,6 +25,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"

View File

@ -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')

View File

@ -46,6 +46,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

View File

@ -1016,6 +1016,28 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-video-plugin-sample (0.0.0):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- react-native-video
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-video/Video (6.2.0):
- DoubleConversion
- glog
@ -1324,6 +1346,7 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- react-native-video (from `../../..`)
- react-native-video-plugin-sample (from `../../react-native-video-plugin-sample`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@ -1441,6 +1464,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
react-native-video:
:path: "../../.."
react-native-video-plugin-sample:
:path: "../../react-native-video-plugin-sample"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
@ -1505,12 +1530,12 @@ SPEC CHECKSUMS:
ExpoModulesCore: 2731dc119f8c1400636a994df4efbc19522defbd
FBLazyVector: 4bc164e5b5e6cfc288d2b5ff28643ea15fa1a589
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
hermes-engine: 01d3e052018c2a13937aca1860fbedbccd4a41b7
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
RCT-Folly: 045d6ecaa59d826c5736dfba0b2f4083ff8d79df
RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47
RCTDeprecation: b03c35057846b685b3ccadc9bfe43e349989cdb2
RCTRequired: 194626909cfa8d39ca6663138c417bc6c431648c
RCTTypeSafety: 552aff5b8e8341660594db00e53ac889682bc120
@ -1535,6 +1560,7 @@ SPEC CHECKSUMS:
React-logger: 29fa3e048f5f67fe396bc08af7606426d9bd7b5d
React-Mapbuffer: 86703e9e4f6522053568300827b436ccc01e1101
react-native-video: 59f262a2d87c998b747ca1f031efb6eba1c156b5
react-native-video-plugin-sample: d3a93b7ad777cad7fa2c30473de75a2635ce5feb
React-nativeconfig: 5d452e509d6fbedc1522e21b566451fc673ac6b7
React-NativeModulesApple: 6560431301ffdab8df6212cc8c8eff779396d8e0
React-perflogger: 32ed45d9cee02cf6639acae34251590dccd30994
@ -1566,6 +1592,6 @@ SPEC CHECKSUMS:
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: 45564236f670899c9739b1581a12b00ead5d391f
PODFILE CHECKSUM: dfb9633fc816e568fd6d90dde654d15acd66faa9
PODFILE CHECKSUM: a73d485df51877001f2b04a5a4379cfa5a3ba8fa
COCOAPODS: 1.15.2

View File

@ -8,15 +8,16 @@
/* Begin PBXBuildFile section */
00E356F31AD99517003FC87E /* videoplayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* videoplayerTests.m */; };
11C6209C7B72C624AC36CAD1 /* Pods_videoplayer_videoplayerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594390EE3512A597F6D2CFC8 /* Pods_videoplayer_videoplayerTests.framework */; };
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
208BB513171FFCC3277F9E0F /* Pods_videoplayer_videoplayerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5C7FBD972AC23420C9CAEF6 /* Pods_videoplayer_videoplayerTests.framework */; };
20E2D2234B216472515590E5 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B7A50CD29E62AE55CDBAC5 /* ExpoModulesProvider.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
8564D8A0ECE6B35EF7A78EDB /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6DF30451E50AB13568EFC /* ExpoModulesProvider.swift */; };
A64041D5CF85945B698F6FD0 /* Pods_videoplayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4610D42AC113C0F184AAF5BC /* Pods_videoplayer.framework */; };
C57DB7DC75FFA5378D941129 /* Pods_videoplayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 10FD7EDE51B5059CB0982AD2 /* Pods_videoplayer.framework */; };
DA6F026ACB11B4361D7006B9 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 06EB80F4634394ABC14C45DC /* PrivacyInfo.xcprivacy */; };
EC73F7EE64DE3B7F743B618D /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -33,22 +34,22 @@
00E356EE1AD99517003FC87E /* videoplayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = videoplayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* videoplayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = videoplayerTests.m; sourceTree = "<group>"; };
058979377AFD7ECE5B23DBEB /* Pods-videoplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.release.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.release.xcconfig"; sourceTree = "<group>"; };
06EB80F4634394ABC14C45DC /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = videoplayer/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
10FD7EDE51B5059CB0982AD2 /* Pods_videoplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
13B07F961A680F5B00A75B9A /* videoplayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = videoplayer.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = videoplayer/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = videoplayer/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = videoplayer/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = videoplayer/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = videoplayer/main.m; sourceTree = "<group>"; };
327740C386721461467B91B2 /* Pods-videoplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.release.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.release.xcconfig"; sourceTree = "<group>"; };
4610D42AC113C0F184AAF5BC /* Pods_videoplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5112808AA45F803BD0D3F411 /* Pods-videoplayer-videoplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.release.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.release.xcconfig"; sourceTree = "<group>"; };
594390EE3512A597F6D2CFC8 /* Pods_videoplayer_videoplayerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer_videoplayerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6E6DE29C8B861F4D5A4BBDEB /* Pods-videoplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.debug.xcconfig"; sourceTree = "<group>"; };
2F5C4E6DD1564FCB6C9B7B94 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.debug.xcconfig"; sourceTree = "<group>"; };
7AF6DF30451E50AB13568EFC /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-videoplayer-videoplayerTests/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = videoplayer/LaunchScreen.storyboard; sourceTree = "<group>"; };
9C018F4E223E0E71BA85ABC9 /* Pods-videoplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.debug.xcconfig"; sourceTree = "<group>"; };
A5C7FBD972AC23420C9CAEF6 /* Pods_videoplayer_videoplayerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer_videoplayerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B9B7A50CD29E62AE55CDBAC5 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-videoplayer/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
CBE734469FBE698BF938A7EF /* Pods-videoplayer-videoplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.debug.xcconfig"; sourceTree = "<group>"; };
CF1F0C5E1D8D8D557C4C7043 /* Pods-videoplayer-videoplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.release.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@ -57,7 +58,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
11C6209C7B72C624AC36CAD1 /* Pods_videoplayer_videoplayerTests.framework in Frameworks */,
208BB513171FFCC3277F9E0F /* Pods_videoplayer_videoplayerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -65,7 +66,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A64041D5CF85945B698F6FD0 /* Pods_videoplayer.framework in Frameworks */,
EC73F7EE64DE3B7F743B618D /* BuildFile in Frameworks */,
C57DB7DC75FFA5378D941129 /* Pods_videoplayer.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -107,8 +109,8 @@
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
4610D42AC113C0F184AAF5BC /* Pods_videoplayer.framework */,
594390EE3512A597F6D2CFC8 /* Pods_videoplayer_videoplayerTests.framework */,
10FD7EDE51B5059CB0982AD2 /* Pods_videoplayer.framework */,
A5C7FBD972AC23420C9CAEF6 /* Pods_videoplayer_videoplayerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -165,10 +167,10 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup;
children = (
6E6DE29C8B861F4D5A4BBDEB /* Pods-videoplayer.debug.xcconfig */,
327740C386721461467B91B2 /* Pods-videoplayer.release.xcconfig */,
CBE734469FBE698BF938A7EF /* Pods-videoplayer-videoplayerTests.debug.xcconfig */,
5112808AA45F803BD0D3F411 /* Pods-videoplayer-videoplayerTests.release.xcconfig */,
9C018F4E223E0E71BA85ABC9 /* Pods-videoplayer.debug.xcconfig */,
058979377AFD7ECE5B23DBEB /* Pods-videoplayer.release.xcconfig */,
2F5C4E6DD1564FCB6C9B7B94 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */,
CF1F0C5E1D8D8D557C4C7043 /* Pods-videoplayer-videoplayerTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -188,13 +190,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "videoplayerTests" */;
buildPhases = (
C0C880E0881EE98C1792E57D /* [CP] Check Pods Manifest.lock */,
05848CC282AE20BB2B2AA52D /* [CP] Check Pods Manifest.lock */,
E0436766C647BDEAF9FD5ED3 /* [Expo] Configure project */,
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
E3461B2529EDAA05BCA42787 /* [CP] Embed Pods Frameworks */,
0765D094F5C79DBE6CC6386B /* [CP] Copy Pods Resources */,
0F69B47FEB727B4EBBAC0C93 /* [CP] Embed Pods Frameworks */,
8DEA7E188641F8CD9B4543DD /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -210,15 +212,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "videoplayer" */;
buildPhases = (
66FA7E36D064B737784DDFB7 /* [CP] Check Pods Manifest.lock */,
4BC7B73D9362CA23BDA1E909 /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
43E82399B51FE7A2CADEE958 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
189A4605ED0B2F88CF1DCFA8 /* [CP] Embed Pods Frameworks */,
F467D0E16E9AD72917847B43 /* [CP] Copy Pods Resources */,
5580EE7ED1DD133A3CB36FB0 /* [CP] Embed Pods Frameworks */,
4617BBDCD64674510B35868A /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -302,82 +304,7 @@
shellPath = /bin/sh;
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli')\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n";
};
0765D094F5C79DBE6CC6386B /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
189A4605ED0B2F88CF1DCFA8 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
43E82399B51FE7A2CADEE958 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-videoplayer/expo-configure-project.sh\"\n";
};
66FA7E36D064B737784DDFB7 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-videoplayer-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C0C880E0881EE98C1792E57D /* [CP] Check Pods Manifest.lock */ = {
05848CC282AE20BB2B2AA52D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -399,6 +326,115 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
0F69B47FEB727B4EBBAC0C93 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
43E82399B51FE7A2CADEE958 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-videoplayer/expo-configure-project.sh\"\n";
};
4617BBDCD64674510B35868A /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources.sh\"\n";
showEnvVarsInLog = 0;
};
4BC7B73D9362CA23BDA1E909 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-videoplayer-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
5580EE7ED1DD133A3CB36FB0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
8DEA7E188641F8CD9B4543DD /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
E0436766C647BDEAF9FD5ED3 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -418,40 +454,6 @@
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-videoplayer-videoplayerTests/expo-configure-project.sh\"\n";
};
E3461B2529EDAA05BCA42787 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
F467D0E16E9AD72917847B43 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources.sh\"\n";
showEnvVarsInLog = 0;
};
FD10A7F022414F080027D42C /* Start Packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -506,7 +508,7 @@
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = CBE734469FBE698BF938A7EF /* Pods-videoplayer-videoplayerTests.debug.xcconfig */;
baseConfigurationReference = 2F5C4E6DD1564FCB6C9B7B94 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
GCC_PREPROCESSOR_DEFINITIONS = (
@ -534,7 +536,7 @@
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5112808AA45F803BD0D3F411 /* Pods-videoplayer-videoplayerTests.release.xcconfig */;
baseConfigurationReference = CF1F0C5E1D8D8D557C4C7043 /* Pods-videoplayer-videoplayerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
@ -559,7 +561,7 @@
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6E6DE29C8B861F4D5A4BBDEB /* Pods-videoplayer.debug.xcconfig */;
baseConfigurationReference = 9C018F4E223E0E71BA85ABC9 /* Pods-videoplayer.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@ -587,7 +589,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 327740C386721461467B91B2 /* Pods-videoplayer.release.xcconfig */;
baseConfigurationReference = 058979377AFD7ECE5B23DBEB /* Pods-videoplayer.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;

View File

@ -4,6 +4,7 @@
"compilerOptions": {
"paths": {
"react-native-video": ["../../src/index"],
"react-native-video-plugin-sample": ["../react-native-video-plugin-sample/src/index"],
}
},
"jsx": "react",

View File

@ -0,0 +1,106 @@
buildscript {
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["VideoPluginSample_kotlinVersion"]
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.2.1"
// noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
def reactNativeArchitectures() {
def value = rootProject.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
if (isNewArchitectureEnabled()) {
apply plugin: "com.facebook.react"
}
def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["VideoPluginSample_" + name]
}
def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["VideoPluginSample_" + name]).toInteger()
}
def supportsNamespace() {
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
def major = parsed[0].toInteger()
def minor = parsed[1].toInteger()
// Namespace support was added in 7.3.0
return (major == 7 && minor >= 3) || major >= 8
}
android {
if (supportsNamespace()) {
namespace "com.videopluginsample"
sourceSets {
main {
manifest.srcFile "src/main/AndroidManifestNew.xml"
}
}
}
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
defaultConfig {
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
}
buildTypes {
release {
minifyEnabled false
}
}
lintOptions {
disable "GradleCompatible"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
mavenCentral()
google()
}
def safeExtGet(prop) {
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : project.properties["RNVideo_" + prop]
}
def kotlin_version = getExtOrDefault("kotlinVersion")
def media3_version = safeExtGet('media3Version')
dependencies {
// For < 0.71, this will be from the local maven repo
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.media3:media3-exoplayer:$media3_version"
implementation project(':react-native-video')
}

View File

@ -0,0 +1,5 @@
VideoPluginSample_kotlinVersion=1.7.0
VideoPluginSample_minSdkVersion=21
VideoPluginSample_targetSdkVersion=31
VideoPluginSample_compileSdkVersion=31
VideoPluginSample_ndkversion=21.4.7075529

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.videopluginsample">
</manifest>

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,50 @@
package com.videopluginsample
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.util.EventLogger
import com.brentvatne.common.toolbox.DebugLog
import com.brentvatne.react.RNVPlugin
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
class VideoPluginSampleModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext), RNVPlugin, Player.Listener {
private val debugEventLogger = EventLogger("RNVPluginSample")
override fun getName(): String {
return NAME
}
@ReactMethod
fun setMetadata(promise: Promise) {
promise.resolve(true)
}
companion object {
const val NAME = "VideoPluginSample"
const val TAG = "VideoPluginSampleModule"
}
override fun onPlayerError(error: PlaybackException) {
DebugLog.e(TAG, "onPlayerError: " + error.errorCodeName)
}
override fun onInstanceCreated(id: String, player: Any) {
if (player is ExoPlayer) {
player.addAnalyticsListener(debugEventLogger)
player.addListener(this)
}
}
override fun onInstanceRemoved(id: String, player: Any) {
if (player is ExoPlayer) {
player.removeAnalyticsListener(debugEventLogger)
}
}
}

View File

@ -0,0 +1,19 @@
package com.videopluginsample
import com.brentvatne.react.ReactNativeVideoManager
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class VideoPluginSamplePackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val plugin = VideoPluginSampleModule(reactContext)
ReactNativeVideoManager.getInstance().registerPlugin(plugin)
return listOf(plugin)
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}

View File

@ -0,0 +1,2 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTViewManager.h>

View File

@ -0,0 +1,14 @@
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(VideoPluginSample, NSObject)
RCT_EXTERN_METHOD(setMetadata:
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
@end

View File

@ -0,0 +1,67 @@
import react_native_video
import AVFoundation
import AVKit
@objc(VideoPluginSample)
class VideoPluginSample: NSObject, RNVPlugin {
private var _playerRateChangeObserver: NSKeyValueObservation?
private var _playerCurrentItemChangeObserver: NSKeyValueObservation?
private var _playerItemStatusObserver: NSKeyValueObservation?
/**
* create an init function to register the plugin
*/
override init() {
super.init()
ReactNativeVideoManager.shared.registerPlugin(plugin: self)
}
@objc(withResolver:withRejecter:)
func setMetadata(resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
resolve(true)
}
/*
* Handlers called on player creation and destructon
*/
func onInstanceCreated(id: String, player: Any) {
if player is AVPlayer {
let avPlayer = player as! AVPlayer
NSLog("plug onInstanceCreated")
_playerRateChangeObserver = avPlayer.observe(\.rate, options: [.old], changeHandler: handlePlaybackRateChange)
_playerCurrentItemChangeObserver = avPlayer.observe(\.currentItem, options: [.old], changeHandler: handleCurrentItemChange)
}
}
func onInstanceRemoved(id: String, player: Any) {
if player is AVPlayer {
let avPlayer = player as! AVPlayer
NSLog("plug onInstanceRemoved")
_playerRateChangeObserver?.invalidate()
_playerCurrentItemChangeObserver?.invalidate()
}
}
/**
* custom functions to be able to track AVPlayer state change
*/
func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange<Float>) {
NSLog("plugin: handlePlaybackRateChange \(change.oldValue)")
}
func handlePlayerItemStatusChange(playerItem: AVPlayerItem, change _: NSKeyValueObservedChange<AVPlayerItem.Status>) {
NSLog("plugin: handlePlayerItemStatusChange \(playerItem.status)")
}
func handleCurrentItemChange(player: AVPlayer, change: NSKeyValueObservedChange<AVPlayerItem?>) {
NSLog("plugin: handleCurrentItemChange \(player.currentItem)")
guard let playerItem = player.currentItem else {
_playerItemStatusObserver?.invalidate()
return
}
_playerItemStatusObserver = playerItem.observe(\.status, options: [.new, .old], changeHandler: handlePlayerItemStatusChange)
}
}

View File

@ -0,0 +1,9 @@
{
"name": "react-native-video-plugin-sample",
"version": "0.0.0",
"description": "sample subpackage for react native video plugin",
"main": "src/index",
"author": " <> ()",
"license": "UNLICENSED",
"homepage": "#readme"
}

View File

@ -0,0 +1,42 @@
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
Pod::Spec.new do |s|
s.name = "react-native-video-plugin-sample"
s.version = package["version"]
s.summary = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.authors = package["author"]
s.platforms = { :ios => min_ios_version_supported }
s.source = { :git => ".git", :tag => "#{s.version}" }
s.source_files = "ios/**/*.{h,m,mm,swift}"
s.dependency "react-native-video"
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true)
install_modules_dependencies(s)
else
s.dependency "React-Core"
# Don't install the dependencies when we run `pod install` in the old architecture.
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
s.dependency "React-Codegen"
s.dependency "RCT-Folly"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
end
end
end

View File

@ -0,0 +1,22 @@
import { NativeModules, Platform } from 'react-native';
const LINKING_ERROR =
`The package 'react-native-video-plugin-sample' doesn't seem to be linked. Make sure: \n\n` +
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
'- You rebuilt the app after installing the package\n' +
'- You are not using Expo Go\n';
const VideoPluginSample = NativeModules.VideoPluginSample
? NativeModules.VideoPluginSample
: new Proxy(
{},
{
get() {
throw new Error(LINKING_ERROR);
},
}
);
export function multiply(a: number, b: number): Promise<number> {
return VideoPluginSample.multiply(a, b);
}

View File

@ -81,6 +81,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
}
}
private let instanceId = UUID().uuidString
private var _isBuffering = false {
didSet {
onVideoBuffer?(["isBuffering": _isBuffering, "target": reactTag as Any])
@ -184,6 +186,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
init(eventDispatcher: RCTEventDispatcher!) {
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
ReactNativeVideoManager.shared.registerView(newInstance: self)
#if USE_GOOGLE_IMA
_imaAdsManager = RCTIMAAdsManager(video: self, pipEnabled: isPipEnabled)
#endif
@ -263,6 +266,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
#if os(iOS)
_pip = nil
#endif
ReactNativeVideoManager.shared.unregisterView(newInstance: self)
}
// MARK: - App lifecycle handlers
@ -462,6 +466,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
if _player == nil {
_player = AVPlayer()
ReactNativeVideoManager.shared.onInstanceCreated(id: instanceId, player: _player)
_player!.replaceCurrentItem(with: playerItem)
if _showNotificationControls {
@ -1261,6 +1267,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
_selectedAudioTrackCriteria = nil
_presentingViewController = nil
ReactNativeVideoManager.shared.onInstanceRemoved(id: instanceId, player: _player)
_player = nil
_resouceLoaderDelegate = nil
_playerObserver.clearPlayer()

23
ios/Video/RNVPlugin.swift Normal file
View File

@ -0,0 +1,23 @@
//
// RNVPlugin.swift
// react-native-video
//
import Foundation
public protocol RNVPlugin {
/**
* Function called when a new player is created
* @param id: a random string identifying the player
* @param player: the instantiated player reference
*/
func onInstanceCreated(id: String, player: Any)
/**
* Function called when a player should be destroyed
* when this callback is called, the 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
*/
func onInstanceRemoved(id: String, player: Any)
}

View File

@ -0,0 +1,53 @@
//
// ReactNativeVideoManager.swift
// react-native-video
//
import Foundation
public class ReactNativeVideoManager: RNVPlugin {
private let expectedMaxVideoCount = 10
// create a private initializer
private init() {}
public static let shared: ReactNativeVideoManager = .init()
var instanceList: [RCTVideo] = Array()
var pluginList: [RNVPlugin] = Array()
/**
* register a new ReactExoplayerViewManager in the managed list
*/
func registerView(newInstance: RCTVideo) {
if instanceList.count > expectedMaxVideoCount {
DebugLog("multiple Video displayed ?")
}
instanceList.append(newInstance)
}
/**
* unregister existing ReactExoplayerViewManager in the managed list
*/
func unregisterView(newInstance: RCTVideo) {
if let i = instanceList.firstIndex(of: newInstance) {
instanceList.remove(at: i)
}
}
/**
* register a new plugin in the managed list
*/
public func registerPlugin(plugin: RNVPlugin) {
pluginList.append(plugin)
return
}
public func onInstanceCreated(id: String, player: Any) {
pluginList.forEach { it in it.onInstanceCreated(id: id, player: player) }
}
public func onInstanceRemoved(id: String, player: Any) {
pluginList.forEach { it in it.onInstanceRemoved(id: id, player: player) }
}
}