feat(ios): add overridePlayerAsset to AVPlayerPlugin (#4522)

This commit is contained in:
Krzysztof Moch
2025-04-18 18:02:55 +02:00
committed by GitHub
parent 2bdc0daf2e
commit b1b3db3010
10 changed files with 196 additions and 81 deletions
+60 -9
View File
@@ -209,29 +209,80 @@ class MyAVPlayerAnalyticsPlugin: RNVAVPlayerPlugin {
override func onInstanceRemoved(id: String, player: AVPlayer) { override func onInstanceRemoved(id: String, player: AVPlayer) {
// Handle AVPlayer removal with type-safe access // Handle AVPlayer removal with type-safe access
} }
/// Optionally override the asset used by the player before playback starts
override func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? {
// Return a modified asset or nil to use the default
return nil
}
} }
``` ```
The `RNVPlugin` class defines two methods: The `RNVAVPlayerPlugin` class defines several extension points:
```swift ```swift
/** /**
* Function called when a new player is created * Function called when a new AVPlayer instance is created
* @param id: a random string identifying the player * @param id: a random string identifying the player
* @param player: the instantiated player reference * @param player: the instantiated AVPlayer
*/ */
open func onInstanceCreated(id: String, player: Any) { /* no-op */ } open func onInstanceCreated(id: String, player: AVPlayer) { /* no-op */ }
/** /**
* Function called when a player should be destroyed * Function called when an AVPlayer instance is being removed
* 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 id: a random string identifying the player
* @param player: the player to release * @param player: the AVPlayer to release
*/ */
open func onInstanceRemoved(id: String, player: Any) { /* no-op */ } open func onInstanceRemoved(id: String, player: AVPlayer) { /* no-op */ }
/**
* Optionally override the asset used by the player before playback starts.
* Allows you to modify or replace the AVAsset before it is used to create the AVPlayerItem.
* Return nil to use the default asset.
*
* @param source: The VideoSource describing the video (uri, type, headers, etc.)
* @param asset: The AVAsset prepared by the player
* @return: OverridePlayerAssetResult if you want to override, or nil to use the default
*/
open func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? { nil }
``` ```
##### `OverridePlayerAssetResult` and `OverridePlayerAssetType`
To override the asset, return an `OverridePlayerAssetResult`:
```swift
public struct OverridePlayerAssetResult {
public let type: OverridePlayerAssetType
public let asset: AVAsset
public init(type: OverridePlayerAssetType, asset: AVAsset) {
self.type = type
self.asset = asset
}
}
public enum OverridePlayerAssetType {
case partial // Return a partially modified asset; will go through the default prepare process
case full // Return a fully modified asset; will skip the default prepare process
}
```
- Use `.partial` if you want the asset to continue through the player's normal preparation (e.g., for text tracks or metadata injection).
- Use `.full` if you want to provide a fully prepared asset that will be used as-is for playback.
**Example:**
```swift
override func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? {
// Example: Replace the asset URL
let newAsset = AVAsset(url: URL(string: "https://example.com/override.mp4")!)
return Result(type: .full, asset: newAsset)
}
```
> Only one plugin can override the player asset at a time. If multiple plugins implement this, only the first will be used.
### 3. Register the Plugin ### 3. Register the Plugin
To register the plugin in `react-native-video`, call: To register the plugin in `react-native-video`, call:
+67 -67
View File
@@ -1,19 +1,19 @@
PODS: PODS:
- boost (1.84.0) - boost (1.84.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EXConstants (17.0.3): - EXConstants (17.0.8):
- ExpoModulesCore - ExpoModulesCore
- Expo (52.0.7): - Expo (52.0.46):
- ExpoModulesCore - ExpoModulesCore
- ExpoAsset (11.0.1): - ExpoAsset (11.0.5):
- ExpoModulesCore - ExpoModulesCore
- ExpoFileSystem (18.0.3): - ExpoFileSystem (18.0.12):
- ExpoModulesCore - ExpoModulesCore
- ExpoFont (13.0.1): - ExpoFont (13.0.4):
- ExpoModulesCore - ExpoModulesCore
- ExpoKeepAwake (14.0.1): - ExpoKeepAwake (14.0.3):
- ExpoModulesCore - ExpoModulesCore
- ExpoModulesCore (2.0.3): - ExpoModulesCore (2.2.3):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
@@ -36,7 +36,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- ExpoSplashScreen (0.29.11): - ExpoSplashScreen (0.29.24):
- ExpoModulesCore - ExpoModulesCore
- FBLazyVector (0.76.2-0) - FBLazyVector (0.76.2-0)
- fmt (9.1.0) - fmt (9.1.0)
@@ -1278,7 +1278,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- react-native-video (6.10.0): - react-native-video (6.12.0):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
@@ -1291,7 +1291,7 @@ PODS:
- React-featureflags - React-featureflags
- React-graphics - React-graphics
- React-ImageManager - React-ImageManager
- react-native-video/Video (= 6.10.0) - react-native-video/Video (= 6.12.0)
- React-NativeModulesApple - React-NativeModulesApple
- React-RCTFabric - React-RCTFabric
- React-rendererdebug - React-rendererdebug
@@ -1322,7 +1322,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- react-native-video/Video (6.10.0): - react-native-video/Video (6.12.0):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
@@ -1851,80 +1851,80 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost: ad4d3a971e4f0b520a127c02c8c3f98b43394c15 boost: ad4d3a971e4f0b520a127c02c8c3f98b43394c15
DoubleConversion: 9a8708fb8299350a38a3425761aaea31263b7443 DoubleConversion: 9a8708fb8299350a38a3425761aaea31263b7443
EXConstants: dd2fe64c6cdb1383b694c309a63028a8e9f2be6d EXConstants: fcfc75800824ac2d5c592b5bc74130bad17b146b
Expo: 46cbe74ce0d0f4a4d7b726e90693eb8dfcec6de0 Expo: a6ff273c618506b12129a0d06f2a08201bfbcf43
ExpoAsset: 8138f2a9ec55ae1ad7c3871448379f7d97692d15 ExpoAsset: 48386d40d53a8c1738929b3ed509bcad595b5516
ExpoFileSystem: cc31b7a48031ab565f9eb5c2b61aa08d774a271a ExpoFileSystem: 42d363d3b96f9afab980dcef60d5657a4443c655
ExpoFont: 7522d869d84ee2ee8093ee997fef5b86f85d856b ExpoFont: f354e926f8feae5e831ec8087f36652b44a0b188
ExpoKeepAwake: 783e68647b969b210a786047c3daa7b753dcac1f ExpoKeepAwake: b0171a73665bfcefcfcc311742a72a956e6aa680
ExpoModulesCore: 2d1df04dc27f91d8b383c0ec7f1d121e3d9b7f68 ExpoModulesCore: 15f60d00e33ca0cea27147543877ca6e70c42ef5
ExpoSplashScreen: 26cee50e9e95572baf87cd3a8ccaf2ffc3856422 ExpoSplashScreen: 399ee9f85b6c8a61b965e13a1ecff8384db591c2
FBLazyVector: 6bbb8b88508d32cdf0ffb7ceddedf6aca79eff14 FBLazyVector: 6bbb8b88508d32cdf0ffb7ceddedf6aca79eff14
fmt: a150d37a5595336f19c91e6f1db7fcf17d5eb99f fmt: a150d37a5595336f19c91e6f1db7fcf17d5eb99f
glog: 9ab333e271c177cecb6362fe27832c88667cb7e9 glog: 9ab333e271c177cecb6362fe27832c88667cb7e9
hermes-engine: 7bc33a9a81e6a58b3bc3426105ecd59981ac730e hermes-engine: 7bc33a9a81e6a58b3bc3426105ecd59981ac730e
RCT-Folly: 2512380c804686400e7f60b9e8b60525df4a6191 RCT-Folly: 839d900d8e4d2b726f3277dd085d97c8904f9eac
RCTDeprecation: b90bc991c207fa777f3d4f0d7f36d7473e62319a RCTDeprecation: b90bc991c207fa777f3d4f0d7f36d7473e62319a
RCTRequired: c8fda281396e4e5cf96e3ab216c05986fb37ca3e RCTRequired: c8fda281396e4e5cf96e3ab216c05986fb37ca3e
RCTTypeSafety: 28a29fb79051f043943135d2e4403e77d3b7ec21 RCTTypeSafety: 28a29fb79051f043943135d2e4403e77d3b7ec21
React: dade949d9edcee964714fe90a2892beef3d3a54b React: dade949d9edcee964714fe90a2892beef3d3a54b
React-callinvoker: a6517e487e1645d2bc627be8d86b1f8dd5278bb5 React-callinvoker: a6517e487e1645d2bc627be8d86b1f8dd5278bb5
React-Core: 7010fbd0ed122476aa2cf7aa9619ec9ffb73c551 React-Core: 3b252d6ed5c2b9e50ed245f95aa4de1f6b5849ab
React-CoreModules: 64eea3d14266e79f8362da71623a5157a7a6c41f React-CoreModules: 5df4a1fa73a93ca46c33f22148b4f4693b64364e
React-cxxreact: 01f36b93aa3c923f7607c50a43cc2b5a06129f07 React-cxxreact: e8b2b06dfbb9d646d9b45a29c3fc67f230e76fa9
React-debug: 25139c51b9fda09bb1745952c81cea9f46225d6a React-debug: 25139c51b9fda09bb1745952c81cea9f46225d6a
React-defaultsnativemodule: 3805189f0d7bce05b3650d364676cf9b49e75eab React-defaultsnativemodule: 6a217b48ce4eb487e7f0a9697ba6513a274cdbdc
React-domnativemodule: abb803da2e62c1acbae61d277fd044fe77f6a9af React-domnativemodule: 64d0ffb108dc00a1275338afdfb78766fbd799d0
React-Fabric: c8d1c4207248fbaf9c046d92d81bab67d4694122 React-Fabric: cb88e6fe3e97be05b425413dbba4fb6cab8e9b34
React-FabricComponents: 7147628a2fec488771369e0a0dead89ad9649e0e React-FabricComponents: c5a6e9c81f9a64521ac8abf9ca2f2532e1398b86
React-FabricImage: f814221b18a7d560baaa5fb807895c4c345036be React-FabricImage: 724ae12ceb53b5f01dfc03d0acd63e392487713f
React-featureflags: b684bbc804b9c85754fd009167a527a6eadbd9d5 React-featureflags: b684bbc804b9c85754fd009167a527a6eadbd9d5
React-featureflagsnativemodule: 8b29cc7a659d17d0072924a83de9cf9a62a4d147 React-featureflagsnativemodule: 46d989a753848048218ea29a30c081274c42e661
React-graphics: 4d7d1fa0ea456c398de7b1f40f708885bccec2e8 React-graphics: accc162849d86b3a8607ce80dd49afc129f35f66
React-hermes: 981bd8b0f89a50a8fd06079bbdc4b11003135021 React-hermes: 2702dda9e6550e7bfcd6d11ed2dd5ac79ee824a7
React-idlecallbacksnativemodule: 12e7223f085d9d0b82aa0a10b0691294b11c792e React-idlecallbacksnativemodule: c271aad98748669cf8a045b0559a0d3fd67013d2
React-ImageManager: 4300a7a83ee603351659dade2e61e673ae3b88d2 React-ImageManager: 6eb6ea557f32850680896907964fc0a8515721db
React-jserrorhandler: 40984dfabb1ab0b2e21d9d21af66530d5309aad0 React-jserrorhandler: 6236637935a035413e9ada45c7a08a2a58fa7212
React-jsi: ecb93733e8efcd240957abf32b33624ce207d1f9 React-jsi: a18137983c0690f3e29882128aa7daf624d2fdac
React-jsiexecutor: 7ceddf35714b4c4a95ea1f590a16d96b1d577c49 React-jsiexecutor: 75909d0eeac14133f33a0446b776388591e1c7c7
React-jsinspector: fe8b01493fb3cb8c6b946cb24c3e83e5d774fb1e React-jsinspector: db3334448e910b60b59cd1809dcf747970525077
React-jsitracing: 47a37e9a623aadd867574d8143fa314df57f1f49 React-jsitracing: 545fc999f9db67b157323f439ff964caccae2c48
React-logger: e928ead07d07fbe16f5bebc7ee004bfb7bf731d5 React-logger: c1804a09cb1c3486fcd9c2b4753ba7b5b9ae1c38
React-Mapbuffer: 08b067fc26d19868b0a1fea3586d92afff4e8e4f React-Mapbuffer: 2c717de369c66af62d34cdf52202da3951feab59
React-microtasksnativemodule: 309d3c6653f4ffcb8a3956604e07ab19b006ef82 React-microtasksnativemodule: c285dc3fa70ea3099d8ee0887aab0fc53f3ef791
react-native-video: 25ecb15d6ac0f97e2d863cbe317c70bd6e8f1a6e react-native-video: b18f2dda48b6424cb8d4d6168821223bbf9eb38a
react-native-video-plugin-sample: 1f6b1dda047cdf07f6cd7e730dc92c1349656b1a react-native-video-plugin-sample: 79af31831cddfa916eb264d89b006fdcf227a8f5
React-nativeconfig: e90353e1c5ab46843222e25d529d342c174184f4 React-nativeconfig: e90353e1c5ab46843222e25d529d342c174184f4
React-NativeModulesApple: 5f46e9be9fbff00c706cb31a91753975e99f86a1 React-NativeModulesApple: de38a9d30fafd773bc72e8b078d57de2dfccae7f
React-perflogger: a629f2ce2eeab290cfc851a015c5d804a8b36f20 React-perflogger: 63dd38ee6f413be0aa176de9f6efc65819e289ca
React-performancetimeline: 09eaa1261ce7b3d51dcd8001f922c7b7f018957c React-performancetimeline: 450a56eff8fbe3de667f26d1857f3bd150e037a7
React-RCTActionSheet: 0622ace751a55109844ff5d6239c83ae36f193d6 React-RCTActionSheet: 0622ace751a55109844ff5d6239c83ae36f193d6
React-RCTAnimation: 8651b17476b17d97d7f0fad16d9020f9b1724226 React-RCTAnimation: 90e804d3918a97910c94732f23f1c3d3ac43d1ff
React-RCTAppDelegate: 09147856b3e6c55f45afc32f485697b9c7097830 React-RCTAppDelegate: 7503e65922a7de2dcb04c7ad9dba8d18c0c1084b
React-RCTBlob: 4ce6e176c0b0999b6ae907537c665dec382095ca React-RCTBlob: e0ac1d709fab205330a6a4ff3430b19358fe2aa4
React-RCTFabric: 6d6d776ec2bb746a599ca113da4c9e5e30519175 React-RCTFabric: a308ebf92877e49196d81568a0098bc5a54b0cc2
React-RCTImage: 1ba821a26daf160f596266f78b084df38bad43bf React-RCTImage: 3ebf257e04f4e0d366e8d11e9ee0c9fb1d5318e4
React-RCTLinking: b913efa56e459a7c9bd8575ea35fffcd43d2b226 React-RCTLinking: 86a8f8fb5169e0499d8ff9f17b466103a9c7344f
React-RCTNetwork: ff7b971a79b35407aa7fc84d0d34d4bf87977832 React-RCTNetwork: 9d8dae79abf6281174aabdcd5cb952b125a19834
React-RCTSettings: a40d2c591ea4b3ebeb0d5fca84cef83bf3a808b8 React-RCTSettings: 26320c05739c8dc553e87f8195efb51fa1a4f4d1
React-RCTText: 54e4c01e54f2b323494950100b3f96a44c8d756a React-RCTText: 1b3aafa95d0b8f5546ea8b11f218b97a6c50e6ac
React-RCTVibration: 415d77ace6572495a7fabc0013794eb232263f61 React-RCTVibration: b4b8c7ca82031b6a628d5197db32123786269f83
React-rendererconsistency: abd0c952e6447de1851995630d388adb756c5490 React-rendererconsistency: abd0c952e6447de1851995630d388adb756c5490
React-rendererdebug: 931afa3e69cad5617d70fc8d22a51b8f0b8962ab React-rendererdebug: d2cee4431aae3b94a369922950314bdd11ed4e36
React-rncore: 4fa27b1f5758f1e43daecaca33828b6724ff975e React-rncore: 4fa27b1f5758f1e43daecaca33828b6724ff975e
React-RuntimeApple: bc87436ff646bc38b3b40b5c1b64b3292083b5d6 React-RuntimeApple: 4400921398bc7f37dea96b38408f75fa79a4e087
React-RuntimeCore: 4588a5b4bf68324088d3aba640c7aecda1f8ccf7 React-RuntimeCore: f5d8891ca6c8fd0ea1ce97e1e742f674c6e3f3de
React-runtimeexecutor: 369f2c0dd457ecc25d8aca20717b8b7c655fea30 React-runtimeexecutor: 369f2c0dd457ecc25d8aca20717b8b7c655fea30
React-RuntimeHermes: fa0dbe3e215052f6f2a64961a81292a7d8a29cc1 React-RuntimeHermes: f36d89a2b787488c5c52b56f6e390ea6daad8177
React-runtimescheduler: 0c3791a76e2c851a6c0387549f6288cb9dd6e3d6 React-runtimescheduler: d767ba9dbcc56926ef8e33d9a79ffc73b1f21216
React-timing: d1b4d886b3ad54cb7d7902551d6f326de16b13d3 React-timing: d1b4d886b3ad54cb7d7902551d6f326de16b13d3
React-utils: 5b35e0df4b1e5414ccb7aad3295de9e422d19265 React-utils: b5263797499f58d23f8cdf84a43947fa935144a5
ReactCodegen: 64ba3fd7eb98692015dbd29ca0ca38ea2c5f92c6 ReactCodegen: be823df68a7af474545f249dab02cd94893917e8
ReactCommon: 8676c2ee5eb8c937ea33df7c91f42d4cd9e95e79 ReactCommon: ccfca2ddb61b208d3a9bfbbfc87233656d65a4d8
RNCPicker: f963e01f78e546a93b98aa201501713dbda14e94 RNCPicker: 0e683a85cae99a8a739e97a49f269a4630de0a01
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: aaa386a35c44b356306dafca9e8afd81877fb7ed Yoga: 748384d41d07db337d313e0f37eec81c8c0ae46f
PODFILE CHECKSUM: 90d803972b4acfc1d2bb6dbe7b0713677a2ff655 PODFILE CHECKSUM: 90d803972b4acfc1d2bb6dbe7b0713677a2ff655
COCOAPODS: 1.15.2 COCOAPODS: 1.16.2
+1 -1
View File
@@ -1,4 +1,4 @@
struct AdParams { public struct AdParams {
let adTagUrl: String? let adTagUrl: String?
let adLanguage: String? let adLanguage: String?
@@ -1,4 +1,4 @@
struct CustomMetadata { public struct CustomMetadata {
let title: String? let title: String?
let subtitle: String? let subtitle: String?
let artist: String? let artist: String?
@@ -0,0 +1,24 @@
import AVFoundation
// MARK: - OverridePlayerAssetType
public enum OverridePlayerAssetType {
// Return partially modified asset, that will go through the default prepare process
case partial
// Return fully modified asset, that will skip the default prepare process
case full
}
public typealias OverridePlayerAsset = (VideoSource, AVAsset) async -> OverridePlayerAssetResult
// MARK: - OverridePlayerAssetResult
public struct OverridePlayerAssetResult {
public let type: OverridePlayerAssetType
public let asset: AVAsset
public init(type: OverridePlayerAssetType, asset: AVAsset) {
self.type = type
self.asset = asset
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
struct TextTrack { public struct TextTrack {
let type: String let type: String
let language: String let language: String
let title: String let title: String
+1 -1
View File
@@ -1,4 +1,4 @@
struct VideoSource { public struct VideoSource {
let type: String? let type: String?
let uri: String? let uri: String?
let isNetwork: Bool let isNetwork: Bool
+21 -1
View File
@@ -493,7 +493,19 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
]) ])
if let uri = source.uri, uri.starts(with: "ph://") { if let uri = source.uri, uri.starts(with: "ph://") {
let photoAsset = await RCTVideoUtils.preparePHAsset(uri: uri) guard let photoAsset = await RCTVideoUtils.preparePHAsset(uri: uri) else {
DebugLog("Could not load asset '\(String(describing: _source))'")
throw NSError(domain: "", code: 0, userInfo: nil)
}
if let overridePlayerAsset = await ReactNativeVideoManager.shared.overridePlayerAsset(source: source, asset: photoAsset) {
if overridePlayerAsset.type == .full {
return AVPlayerItem(asset: overridePlayerAsset.asset)
}
return await playerItemPrepareText(source: source, asset: overridePlayerAsset.asset, assetOptions: nil, uri: source.uri ?? "")
}
return await playerItemPrepareText(source: source, asset: photoAsset, assetOptions: nil, uri: source.uri ?? "") return await playerItemPrepareText(source: source, asset: photoAsset, assetOptions: nil, uri: source.uri ?? "")
} }
@@ -530,6 +542,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
) )
} }
if let overridePlayerAsset = await ReactNativeVideoManager.shared.overridePlayerAsset(source: source, asset: asset) {
if overridePlayerAsset.type == .full {
return AVPlayerItem(asset: overridePlayerAsset.asset)
}
return await playerItemPrepareText(source: source, asset: overridePlayerAsset.asset, assetOptions: assetOptions, uri: source.uri ?? "")
}
return await playerItemPrepareText(source: source, asset: asset, assetOptions: assetOptions, uri: source.uri ?? "") return await playerItemPrepareText(source: source, asset: asset, assetOptions: assetOptions, uri: source.uri ?? "")
} }
+8
View File
@@ -36,6 +36,14 @@ open class RNVAVPlayerPlugin: RNVPlugin {
*/ */
open func onInstanceRemoved(id _: String, player _: AVPlayer) { /* no-op */ } open func onInstanceRemoved(id _: String, player _: AVPlayer) { /* no-op */ }
/**
* Function called when creating a new AVPlayerItem
* @param source: The VideoSource describing the video (uri, type, headers, etc.)
* @param asset: The AVAsset prepared by the player
* @return: OverridePlayerAssetResult if you want to override, or nil if you don't
*/
open func overridePlayerAsset(source _: VideoSource, asset _: AVAsset) async -> OverridePlayerAssetResult? { nil }
// MARK: - RNVPlugin methods // MARK: - RNVPlugin methods
override public func onInstanceCreated(id: String, player: Any) { override public func onInstanceCreated(id: String, player: Any) {
+12
View File
@@ -3,6 +3,7 @@
// react-native-video // react-native-video
// //
import AVFoundation
import Foundation import Foundation
public class ReactNativeVideoManager: RNVPlugin { public class ReactNativeVideoManager: RNVPlugin {
@@ -67,6 +68,17 @@ public class ReactNativeVideoManager: RNVPlugin {
return customDRMManager?.1 ?? DRMManager() return customDRMManager?.1 ?? DRMManager()
} }
public func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? {
for plugin in pluginList {
if let avpPlugin = plugin as? RNVAVPlayerPlugin,
let overridePlayerAsset = await avpPlugin.overridePlayerAsset(source: source, asset: asset) {
return overridePlayerAsset
}
}
return nil
}
// MARK: - Helper methods // MARK: - Helper methods
func maybeRegisterAVPlayerPlugin(plugin: RNVPlugin) { func maybeRegisterAVPlayerPlugin(plugin: RNVPlugin) {