bitmovin-player-react-native 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +274 -0
- package/RNBitmovinPlayer.podspec +23 -0
- package/android/build.gradle +55 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +279 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +264 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +139 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +33 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +162 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/extensions/Events.kt +15 -0
- package/ios/Event+JSON.swift +132 -0
- package/ios/PlayerModule.m +61 -0
- package/ios/PlayerModule.swift +347 -0
- package/ios/RCTConvert+BitmovinPlayer.swift +79 -0
- package/ios/RNBitmovinPlayer.h +7 -0
- package/ios/RNBitmovinPlayer.xcodeproj/project.pbxproj +303 -0
- package/ios/RNPlayerView+PlayerListener.swift +83 -0
- package/ios/RNPlayerView.swift +50 -0
- package/ios/RNPlayerViewManager.m +29 -0
- package/ios/RNPlayerViewManager.swift +60 -0
- package/lib/index.d.ts +486 -0
- package/lib/index.js +218 -0
- package/lib/index.mjs +191 -0
- package/package.json +82 -0
- package/src/components/PlayerView/events.ts +69 -0
- package/src/components/PlayerView/index.tsx +106 -0
- package/src/components/PlayerView/native.ts +17 -0
- package/src/components/index.ts +1 -0
- package/src/events.ts +184 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/usePlayer.ts +10 -0
- package/src/hooks/useProxy.ts +36 -0
- package/src/index.ts +5 -0
- package/src/player.ts +229 -0
- package/src/source.ts +95 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
package com.bitmovin.player.reactnative
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.view.ViewGroup
|
|
5
|
+
import android.widget.LinearLayout
|
|
6
|
+
import com.bitmovin.player.PlayerView
|
|
7
|
+
import com.bitmovin.player.api.Player
|
|
8
|
+
import com.bitmovin.player.api.event.Event
|
|
9
|
+
import com.bitmovin.player.api.event.PlayerEvent
|
|
10
|
+
import com.bitmovin.player.api.event.SourceEvent
|
|
11
|
+
import com.bitmovin.player.api.event.on
|
|
12
|
+
import com.bitmovin.player.reactnative.converter.JsonConverter
|
|
13
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
14
|
+
import com.facebook.react.bridge.ReactContext
|
|
15
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Native view wrapper for component instances. It both serves as the main view
|
|
19
|
+
* handled by RN (the actual player view is handled by the RNPlayerViewManager) and
|
|
20
|
+
* exposes player events as bubbling events.
|
|
21
|
+
*/
|
|
22
|
+
@SuppressLint("ViewConstructor")
|
|
23
|
+
class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
|
|
24
|
+
/**
|
|
25
|
+
* Reference to the shared player view set as child.
|
|
26
|
+
*/
|
|
27
|
+
var playerView: PlayerView? = null
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handy property accessor for `playerView`'s player instance.
|
|
31
|
+
*/
|
|
32
|
+
var player: Player?
|
|
33
|
+
get() = playerView?.player
|
|
34
|
+
set(value) {
|
|
35
|
+
playerView?.player = value
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Set the given `playerView` as child and start bubbling events.
|
|
40
|
+
* @param playerView Shared player view instance.
|
|
41
|
+
*/
|
|
42
|
+
fun addPlayerView(playerView: PlayerView) {
|
|
43
|
+
this.playerView = playerView
|
|
44
|
+
if (playerView.parent != this) {
|
|
45
|
+
(playerView.parent as ViewGroup?)?.removeView(playerView)
|
|
46
|
+
addView(playerView)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Try to measure and update this view layout as much as possible to
|
|
52
|
+
* avoid layout problems related to React or old layout values present
|
|
53
|
+
* in `playerView` due to being previously attached to a different parent.
|
|
54
|
+
*/
|
|
55
|
+
override fun requestLayout() {
|
|
56
|
+
super.requestLayout()
|
|
57
|
+
post {
|
|
58
|
+
measure(
|
|
59
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
60
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
|
61
|
+
layout(left, top, right, bottom)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* `onEvent` event callback.
|
|
67
|
+
*/
|
|
68
|
+
private val onEvent: (PlayerEvent) -> Unit = {
|
|
69
|
+
emitEvent("event", it)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* `onPlayerError` event callback.
|
|
74
|
+
*/
|
|
75
|
+
private val onPlayerError: (PlayerEvent.Error) -> Unit = {
|
|
76
|
+
emitEvent("playerError", it)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `onPlayerWarning` event callback.
|
|
81
|
+
*/
|
|
82
|
+
private val onPlayerWarning: (PlayerEvent.Warning) -> Unit = {
|
|
83
|
+
emitEvent("playerWarning", it)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* `onDestroy` event callback.
|
|
88
|
+
*/
|
|
89
|
+
private val onDestroy: (PlayerEvent.Destroy) -> Unit = {
|
|
90
|
+
emitEvent("destroy", it)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* `onMuted` event callback.
|
|
95
|
+
*/
|
|
96
|
+
private val onMuted: (PlayerEvent.Muted) -> Unit = {
|
|
97
|
+
emitEvent("muted", it)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* `onUnmuted` event callback.
|
|
102
|
+
*/
|
|
103
|
+
private val onUnmuted: (PlayerEvent.Unmuted) -> Unit = {
|
|
104
|
+
emitEvent("unmuted", it)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* `onReady` event callback.
|
|
109
|
+
*/
|
|
110
|
+
private val onReady: (PlayerEvent.Ready) -> Unit = {
|
|
111
|
+
emitEvent("ready", it)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* `onPaused` event callback.
|
|
116
|
+
*/
|
|
117
|
+
private val onPaused: (PlayerEvent.Paused) -> Unit = {
|
|
118
|
+
emitEvent("paused", it)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* `onPlay` event callback.
|
|
123
|
+
*/
|
|
124
|
+
private val onPlay: (PlayerEvent.Play) -> Unit = {
|
|
125
|
+
emitEvent("play", it)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* `onPlaying` event callback.
|
|
130
|
+
*/
|
|
131
|
+
private val onPlaying: (PlayerEvent.Playing) -> Unit = {
|
|
132
|
+
emitEvent("playing", it)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* `onPlaybackFinished` event callback.
|
|
137
|
+
*/
|
|
138
|
+
private val onPlaybackFinished: (PlayerEvent.PlaybackFinished) -> Unit = {
|
|
139
|
+
emitEvent("playbackFinished", it)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* `onSeek` event callback.
|
|
144
|
+
*/
|
|
145
|
+
private val onSeek: (PlayerEvent.Seek) -> Unit = {
|
|
146
|
+
emitEvent("seek", it)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* `onSeeked` event callback.
|
|
151
|
+
*/
|
|
152
|
+
private val onSeeked: (PlayerEvent.Seeked) -> Unit = {
|
|
153
|
+
emitEvent("seeked", it)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* `onTimeChanged` event callback.
|
|
158
|
+
*/
|
|
159
|
+
private val onTimeChanged: (PlayerEvent.TimeChanged) -> Unit = {
|
|
160
|
+
emitEvent("timeChanged", it)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* `onSourceLoad` event callback.
|
|
165
|
+
*/
|
|
166
|
+
private val onSourceLoad: (SourceEvent.Load) -> Unit = {
|
|
167
|
+
emitEvent("sourceLoad", it)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* `onSourceLoaded` event callback.
|
|
172
|
+
*/
|
|
173
|
+
private val onSourceLoaded: (SourceEvent.Loaded) -> Unit = {
|
|
174
|
+
emitEvent("sourceLoaded", it)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* `onSourceUnloaded` event callback.
|
|
179
|
+
*/
|
|
180
|
+
private val onSourceUnloaded: (SourceEvent.Unloaded) -> Unit = {
|
|
181
|
+
emitEvent("sourceUnloaded", it)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* `onSourceError` event callback.
|
|
186
|
+
*/
|
|
187
|
+
private val onSourceError: (SourceEvent.Error) -> Unit = {
|
|
188
|
+
emitEvent("sourceError", it)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* `onSourceWarning` event callback.
|
|
193
|
+
*/
|
|
194
|
+
private val onSourceWarning: (SourceEvent.Warning) -> Unit = {
|
|
195
|
+
emitEvent("sourceWarning", it)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Start listening and emitting player events as bubbling events to the js side.
|
|
200
|
+
*/
|
|
201
|
+
fun startBubblingEvents() {
|
|
202
|
+
player?.on(onEvent)
|
|
203
|
+
player?.on(onPlayerError)
|
|
204
|
+
player?.on(onPlayerWarning)
|
|
205
|
+
player?.on(onDestroy)
|
|
206
|
+
player?.on(onMuted)
|
|
207
|
+
player?.on(onUnmuted)
|
|
208
|
+
player?.on(onReady)
|
|
209
|
+
player?.on(onPaused)
|
|
210
|
+
player?.on(onPlay)
|
|
211
|
+
player?.on(onPlaying)
|
|
212
|
+
player?.on(onPlaybackFinished)
|
|
213
|
+
player?.on(onSeek)
|
|
214
|
+
player?.on(onSeeked)
|
|
215
|
+
player?.on(onTimeChanged)
|
|
216
|
+
player?.on(onSourceLoad)
|
|
217
|
+
player?.on(onSourceLoaded)
|
|
218
|
+
player?.on(onSourceUnloaded)
|
|
219
|
+
player?.on(onSourceError)
|
|
220
|
+
player?.on(onSourceWarning)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Stop listening for player events and cease to emit bubbling events.
|
|
225
|
+
*/
|
|
226
|
+
fun stopBubblingEvents() {
|
|
227
|
+
player?.off(onEvent)
|
|
228
|
+
player?.off(onPlayerError)
|
|
229
|
+
player?.off(onPlayerWarning)
|
|
230
|
+
player?.off(onDestroy)
|
|
231
|
+
player?.off(onMuted)
|
|
232
|
+
player?.off(onUnmuted)
|
|
233
|
+
player?.off(onReady)
|
|
234
|
+
player?.off(onPaused)
|
|
235
|
+
player?.off(onPlay)
|
|
236
|
+
player?.off(onPlaying)
|
|
237
|
+
player?.off(onPlaybackFinished)
|
|
238
|
+
player?.off(onSeek)
|
|
239
|
+
player?.off(onSeeked)
|
|
240
|
+
player?.off(onTimeChanged)
|
|
241
|
+
player?.off(onSourceLoad)
|
|
242
|
+
player?.off(onSourceLoaded)
|
|
243
|
+
player?.off(onSourceUnloaded)
|
|
244
|
+
player?.off(onSourceError)
|
|
245
|
+
player?.off(onSourceWarning)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Emits a bubbling event with payload to js.
|
|
250
|
+
* @param event Native event name.
|
|
251
|
+
* @param json Optional js object to be sent as payload.
|
|
252
|
+
*/
|
|
253
|
+
private inline fun <reified E : Event> emitEvent(name: String, event: E) {
|
|
254
|
+
val payload = if (event is PlayerEvent) {
|
|
255
|
+
JsonConverter.fromPlayerEvent(event)
|
|
256
|
+
} else {
|
|
257
|
+
JsonConverter.fromSourceEvent(event as SourceEvent)
|
|
258
|
+
}
|
|
259
|
+
val reactContext = context as ReactContext
|
|
260
|
+
reactContext
|
|
261
|
+
.getJSModule(RCTEventEmitter::class.java)
|
|
262
|
+
.receiveEvent(id, name, payload)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
package com.bitmovin.player.reactnative
|
|
2
|
+
|
|
3
|
+
import android.view.ViewGroup.LayoutParams
|
|
4
|
+
import com.bitmovin.player.PlayerView
|
|
5
|
+
import com.facebook.react.bridge.*
|
|
6
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
7
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
+
import com.facebook.react.uimanager.UIManagerModule
|
|
9
|
+
|
|
10
|
+
class RNPlayerViewManager(private val context: ReactApplicationContext) : SimpleViewManager<RNPlayerView>() {
|
|
11
|
+
/**
|
|
12
|
+
* Native component functions.
|
|
13
|
+
*/
|
|
14
|
+
enum class Commands {
|
|
15
|
+
ATTACH_PLAYER,
|
|
16
|
+
DETACH_PLAYER,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Exported module name to JS.
|
|
21
|
+
*/
|
|
22
|
+
override fun getName() = "NativePlayerView"
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The component's native view factory. RN calls this method multiple times
|
|
26
|
+
* for each component instance.
|
|
27
|
+
*/
|
|
28
|
+
override fun createViewInstance(reactContext: ThemedReactContext) = RNPlayerView(context)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A mapping between a event native identifier and its bubbled version that will
|
|
32
|
+
* be accessed from React.
|
|
33
|
+
*/
|
|
34
|
+
private val bubblingEventsMapping: Map<String, String> = mapOf(
|
|
35
|
+
"event" to "onEvent",
|
|
36
|
+
"playerError" to "onPlayerError",
|
|
37
|
+
"playerWarning" to "onPlayerWarning",
|
|
38
|
+
"destroy" to "onDestroy",
|
|
39
|
+
"muted" to "onMuted",
|
|
40
|
+
"unmuted" to "onUnmuted",
|
|
41
|
+
"ready" to "onReady",
|
|
42
|
+
"paused" to "onPaused",
|
|
43
|
+
"play" to "onPlay",
|
|
44
|
+
"playing" to "onPlaying",
|
|
45
|
+
"playbackFinished" to "onPlaybackFinished",
|
|
46
|
+
"seek" to "onSeek",
|
|
47
|
+
"seeked" to "onSeeked",
|
|
48
|
+
"timeChanged" to "onTimeChanged",
|
|
49
|
+
"sourceLoad" to "onSourceLoad",
|
|
50
|
+
"sourceLoaded" to "onSourceLoaded",
|
|
51
|
+
"sourceUnloaded" to "onSourceUnloaded",
|
|
52
|
+
"sourceError" to "onSourceError",
|
|
53
|
+
"sourceWarning" to "onSourceWarning",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Component's event registry. Bubbling events are directly mapped to react props. No
|
|
58
|
+
* need to use proxy functions or `NativeEventEmitter`.
|
|
59
|
+
* @return map between event names (sent from native code) to js props.
|
|
60
|
+
*/
|
|
61
|
+
override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> =
|
|
62
|
+
bubblingEventsMapping.entries.associate {
|
|
63
|
+
it.key to mapOf(
|
|
64
|
+
"phasedRegistrationNames" to mapOf("bubbled" to it.value)
|
|
65
|
+
)
|
|
66
|
+
}.toMutableMap()
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Component's command registry. They enable granular control over
|
|
70
|
+
* instances of a certain native component from js and give the ability
|
|
71
|
+
* to call 'functions' on them.
|
|
72
|
+
* @return map between names (used in js) and command ids (used in native code).
|
|
73
|
+
*/
|
|
74
|
+
override fun getCommandsMap(): MutableMap<String, Int> = mutableMapOf(
|
|
75
|
+
"attachPlayer" to Commands.ATTACH_PLAYER.ordinal,
|
|
76
|
+
"detachPlayer" to Commands.DETACH_PLAYER.ordinal,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Callback triggered in response to command dispatches from the js side.
|
|
81
|
+
* @param root Root native view of the targeted component.
|
|
82
|
+
* @param commandId Command number identifier. It's a number even though RN sends it as a string.
|
|
83
|
+
* @param args Arguments list sent from the js side.
|
|
84
|
+
*/
|
|
85
|
+
override fun receiveCommand(view: RNPlayerView, commandId: String?, args: ReadableArray?) {
|
|
86
|
+
super.receiveCommand(view, commandId, args)
|
|
87
|
+
commandId?.toInt()?.let {
|
|
88
|
+
when (it) {
|
|
89
|
+
Commands.ATTACH_PLAYER.ordinal -> attachPlayer(view, args?.getString(1))
|
|
90
|
+
Commands.DETACH_PLAYER.ordinal -> detachPlayer(view, args?.getString(1))
|
|
91
|
+
else -> {}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Set the `Player` instance for the target view using `playerId`.
|
|
98
|
+
* @param view Target `RNPlayerView`.
|
|
99
|
+
* @param playerId `Player` instance id inside `PlayerModule`'s registry.
|
|
100
|
+
*/
|
|
101
|
+
private fun attachPlayer(view: RNPlayerView, playerId: String?) {
|
|
102
|
+
uiManager()?.addUIBlock {
|
|
103
|
+
val player = getPlayerModule()?.getPlayer(playerId)
|
|
104
|
+
if (view.playerView != null) {
|
|
105
|
+
view.player = player
|
|
106
|
+
} else {
|
|
107
|
+
val playerView = PlayerView(context, player)
|
|
108
|
+
playerView.layoutParams = LayoutParams(
|
|
109
|
+
LayoutParams.MATCH_PARENT,
|
|
110
|
+
LayoutParams.MATCH_PARENT)
|
|
111
|
+
view.addPlayerView(playerView)
|
|
112
|
+
}
|
|
113
|
+
view.startBubblingEvents()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Stop sending `Player` bubbling events from `PlayerView`.
|
|
119
|
+
* @param view Target `RNPlayerView`.
|
|
120
|
+
* @param playerId `Player` instance id inside `PlayerModule`'s registry.
|
|
121
|
+
*/
|
|
122
|
+
private fun detachPlayer(view: RNPlayerView, playerId: String?) {
|
|
123
|
+
uiManager()?.addUIBlock {
|
|
124
|
+
view.stopBubblingEvents()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Helper function that gets the instantiated `PlayerModule` from modules registry.
|
|
130
|
+
*/
|
|
131
|
+
private fun getPlayerModule(): PlayerModule? =
|
|
132
|
+
context.getNativeModule(PlayerModule::class.java)
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Helper function that returns the initialized `UIManager` instance.
|
|
136
|
+
*/
|
|
137
|
+
private fun uiManager(): UIManagerModule? =
|
|
138
|
+
context.getNativeModule(UIManagerModule::class.java)
|
|
139
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.bitmovin.player.reactnative
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import com.facebook.react.ReactPackage
|
|
5
|
+
import com.facebook.react.bridge.NativeModule
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.uimanager.ReactShadowNode
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* React package registry.
|
|
12
|
+
*/
|
|
13
|
+
class RNPlayerViewPackage : ReactPackage {
|
|
14
|
+
/**
|
|
15
|
+
* Register `RNPlayerViewManager` as a base react native module. This allows
|
|
16
|
+
* accessing methods on `NativePlayerView` on the js side.
|
|
17
|
+
*/
|
|
18
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
|
|
19
|
+
return mutableListOf(
|
|
20
|
+
PlayerModule(reactContext),
|
|
21
|
+
RNPlayerViewManager(reactContext),
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register `RNPlayerViewManager` as a view manager. This allows creating
|
|
27
|
+
* native component instances with `<NativePlayerView {...} />` on the js
|
|
28
|
+
* side.
|
|
29
|
+
*/
|
|
30
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<out View, out ReactShadowNode<*>>> {
|
|
31
|
+
return mutableListOf(RNPlayerViewManager(reactContext))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
package com.bitmovin.player.reactnative.converter
|
|
2
|
+
|
|
3
|
+
import com.bitmovin.player.api.PlayerConfig
|
|
4
|
+
import com.bitmovin.player.api.event.PlayerEvent
|
|
5
|
+
import com.bitmovin.player.api.event.SourceEvent
|
|
6
|
+
import com.bitmovin.player.api.event.data.SeekPosition
|
|
7
|
+
import com.bitmovin.player.api.source.Source
|
|
8
|
+
import com.bitmovin.player.api.source.SourceConfig
|
|
9
|
+
import com.bitmovin.player.api.source.SourceType
|
|
10
|
+
import com.bitmovin.player.reactnative.extensions.getName
|
|
11
|
+
import com.facebook.react.bridge.*
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper class to gather all conversion methods between JS -> Native objects.
|
|
15
|
+
*/
|
|
16
|
+
class JsonConverter {
|
|
17
|
+
companion object {
|
|
18
|
+
/**
|
|
19
|
+
* Converts an arbitrary `json` to `PlayerConfig`.
|
|
20
|
+
* @param json JS object representing the `PlayerConfig`.
|
|
21
|
+
* @return The generated `PlayerConfig` if successful, `null` otherwise.
|
|
22
|
+
*/
|
|
23
|
+
@JvmStatic
|
|
24
|
+
fun toPlayerConfig(json: ReadableMap?): PlayerConfig? {
|
|
25
|
+
if (json != null && json.hasKey("licenseKey")) {
|
|
26
|
+
return PlayerConfig(key = json.getString("licenseKey"))
|
|
27
|
+
}
|
|
28
|
+
return PlayerConfig()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Converts an arbitrary `json` to `SourceConfig`.
|
|
33
|
+
* @param json JS object representing the `SourceConfig`.
|
|
34
|
+
* @return The generated `SourceConfig` if successful, `null` otherwise.
|
|
35
|
+
*/
|
|
36
|
+
@JvmStatic
|
|
37
|
+
fun toSourceConfig(json: ReadableMap?): SourceConfig? {
|
|
38
|
+
val url = json?.getString("url")
|
|
39
|
+
val type = json?.getString("type")
|
|
40
|
+
if (json == null || url == null || type == null) {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
val config = SourceConfig(url, toSourceType(type))
|
|
44
|
+
config.title = json.getString("title")
|
|
45
|
+
config.posterSource = json.getString("poster")
|
|
46
|
+
if (json.hasKey("isPosterPersistent")) {
|
|
47
|
+
config.isPosterPersistent = json.getBoolean("isPosterPersistent")
|
|
48
|
+
}
|
|
49
|
+
return config
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Converts an arbitrary `json` to `SourceType`.
|
|
54
|
+
* @param json JS string representing the `SourceType`.
|
|
55
|
+
* @return The generated `SourceType` if successful or `SourceType.Dash` otherwise.
|
|
56
|
+
*/
|
|
57
|
+
@JvmStatic
|
|
58
|
+
fun toSourceType(json: String?): SourceType = when (json) {
|
|
59
|
+
"dash" -> SourceType.Dash
|
|
60
|
+
"hls" -> SourceType.Hls
|
|
61
|
+
"smooth" -> SourceType.Smooth
|
|
62
|
+
"progressive" -> SourceType.Progressive
|
|
63
|
+
else -> SourceType.Dash
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Converts any given `Source` object into its `json` representation.
|
|
68
|
+
* @param source `Source` object to be converted.
|
|
69
|
+
* @return The `json` representation of the given `Source`.
|
|
70
|
+
*/
|
|
71
|
+
@JvmStatic
|
|
72
|
+
fun fromSource(source: Source?): WritableMap? {
|
|
73
|
+
if (source == null) {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
val json = Arguments.createMap()
|
|
77
|
+
json.putDouble("duration", source.duration)
|
|
78
|
+
json.putBoolean("isActive", source.isActive)
|
|
79
|
+
json.putBoolean("isAttachedToPlayer", source.isAttachedToPlayer)
|
|
80
|
+
json.putInt("loadingState", source.loadingState.ordinal)
|
|
81
|
+
json.putNull("metadata")
|
|
82
|
+
return json
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Converts any given `SeekPosition` object into its `json` representation.
|
|
87
|
+
* @param seekPosition `SeekPosition` object to be converted.
|
|
88
|
+
* @return The `json` representation of the given `SeekPosition`.
|
|
89
|
+
*/
|
|
90
|
+
@JvmStatic
|
|
91
|
+
fun fromSeekPosition(seekPosition: SeekPosition): WritableMap? {
|
|
92
|
+
val json = Arguments.createMap()
|
|
93
|
+
json.putDouble("time", seekPosition.time)
|
|
94
|
+
json.putMap("source", fromSource(seekPosition.source))
|
|
95
|
+
return json
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Converts any given `SourceEvent` object into its `json` representation.
|
|
100
|
+
* @param event `SourceEvent` object to be converted.
|
|
101
|
+
* @return The `json` representation of the given `SourceEvent`.
|
|
102
|
+
*/
|
|
103
|
+
@JvmStatic
|
|
104
|
+
fun fromSourceEvent(event: SourceEvent): WritableMap? {
|
|
105
|
+
val json = Arguments.createMap()
|
|
106
|
+
json.putString("name", event.getName())
|
|
107
|
+
json.putDouble("timestamp", event.timestamp.toDouble())
|
|
108
|
+
if (event is SourceEvent.Load) {
|
|
109
|
+
json.putMap("source", fromSource(event.source))
|
|
110
|
+
}
|
|
111
|
+
if (event is SourceEvent.Loaded) {
|
|
112
|
+
json.putMap("source", fromSource(event.source))
|
|
113
|
+
}
|
|
114
|
+
if (event is SourceEvent.Error) {
|
|
115
|
+
json.putInt("code", event.code.value)
|
|
116
|
+
json.putString("message", event.message)
|
|
117
|
+
}
|
|
118
|
+
if (event is SourceEvent.Warning) {
|
|
119
|
+
json.putInt("code", event.code.value)
|
|
120
|
+
json.putString("message", event.message)
|
|
121
|
+
}
|
|
122
|
+
return json
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Converts any given `PlayerEvent` object into its `json` representation.
|
|
127
|
+
* @param event `PlayerEvent` object to be converted.
|
|
128
|
+
* @return The `json` representation of given `PlayerEvent`.
|
|
129
|
+
*/
|
|
130
|
+
@JvmStatic
|
|
131
|
+
fun fromPlayerEvent(event: PlayerEvent): WritableMap? {
|
|
132
|
+
val json = Arguments.createMap()
|
|
133
|
+
json.putString("name", event.getName())
|
|
134
|
+
json.putDouble("timestamp", event.timestamp.toDouble())
|
|
135
|
+
if (event is PlayerEvent.Error) {
|
|
136
|
+
json.putInt("code", event.code.value)
|
|
137
|
+
json.putString("message", event.message)
|
|
138
|
+
}
|
|
139
|
+
if (event is PlayerEvent.Warning) {
|
|
140
|
+
json.putInt("code", event.code.value)
|
|
141
|
+
json.putString("message", event.message)
|
|
142
|
+
}
|
|
143
|
+
if (event is PlayerEvent.Play) {
|
|
144
|
+
json.putDouble("time", event.time)
|
|
145
|
+
}
|
|
146
|
+
if (event is PlayerEvent.Playing) {
|
|
147
|
+
json.putDouble("time", event.time)
|
|
148
|
+
}
|
|
149
|
+
if (event is PlayerEvent.Paused) {
|
|
150
|
+
json.putDouble("time", event.time)
|
|
151
|
+
}
|
|
152
|
+
if (event is PlayerEvent.TimeChanged) {
|
|
153
|
+
json.putDouble("currentTime", event.time)
|
|
154
|
+
}
|
|
155
|
+
if (event is PlayerEvent.Seek) {
|
|
156
|
+
json.putMap("from", fromSeekPosition(event.from))
|
|
157
|
+
json.putMap("to", fromSeekPosition(event.to))
|
|
158
|
+
}
|
|
159
|
+
return json
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package com.bitmovin.player.reactnative.extensions
|
|
2
|
+
|
|
3
|
+
import com.bitmovin.player.api.event.PlayerEvent
|
|
4
|
+
import com.bitmovin.player.api.event.SourceEvent
|
|
5
|
+
|
|
6
|
+
fun PlayerEvent.getName(): String {
|
|
7
|
+
if (this is PlayerEvent.Error || this is PlayerEvent.Warning) {
|
|
8
|
+
return "onPlayer${this.javaClass.simpleName}"
|
|
9
|
+
}
|
|
10
|
+
return "on${this.javaClass.simpleName}"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
fun SourceEvent.getName(): String {
|
|
14
|
+
return "onSource${this.javaClass.simpleName}"
|
|
15
|
+
}
|