-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathPlayerContainerView.kt
More file actions
154 lines (122 loc) · 4.46 KB
/
Copy pathPlayerContainerView.kt
File metadata and controls
154 lines (122 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package com.reactnativestandalonevideoplayer
import android.content.Context
import android.util.Log
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
import com.google.android.exoplayer2.ui.StyledPlayerView
class MyPlayerView(context: Context): StyledPlayerView(context) {
var playerInstance: Int = -1
var isBound: Boolean = false
}
//
class PlayerContainerView: SimpleViewManager<MyPlayerView>() {
companion object {
private const val TAG = "PlayerContainerView"
// Thread-safe pending views management
private val pendingViewsLock = Any()
private val _pendingViews: MutableList<MyPlayerView> = mutableListOf()
fun addPendingView(view: MyPlayerView) = synchronized(pendingViewsLock) {
if (!_pendingViews.contains(view)) {
_pendingViews.add(view)
}
}
fun removePendingView(view: MyPlayerView) = synchronized(pendingViewsLock) {
_pendingViews.remove(view)
}
fun clearPendingViews() = synchronized(pendingViewsLock) {
_pendingViews.clear()
}
fun bindPendingViews() = synchronized(pendingViewsLock) {
val iterator = _pendingViews.iterator()
while (iterator.hasNext()) {
val view = iterator.next()
val player = PlayerVideo.getInstance(view.playerInstance)
if (view.playerInstance >= 0 && player != null) {
val targetPlayer = if (view.isBound) player.player else null
view.player = targetPlayer
view.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
(targetPlayer as? ExoPlayer)?.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
iterator.remove()
}
}
}
fun hasPendingViews(): Boolean = synchronized(pendingViewsLock) {
_pendingViews.isNotEmpty()
}
}
init {
Log.d(TAG, "init PlayerContainerView")
}
override fun getName() = "RNTPlayerVideoView"
@ReactProp(name = "isBoundToPlayer")
fun boundToPlayer(view: MyPlayerView, isBoundToPlayer: Boolean) {
Log.d(TAG, "boundToPlayer = $isBoundToPlayer")
if (view.isBound != isBoundToPlayer) {
view.isBound = isBoundToPlayer
setup(view)
}
}
@ReactProp(name = "playerInstance")
fun setPlayerInstance(view: MyPlayerView, instance: Int) {
Log.d(TAG, "playerInstance = $instance")
if (view.playerInstance != instance) {
view.playerInstance = instance
setup(view)
}
}
private fun setup(view: MyPlayerView) {
Log.d(TAG, "setup isBound=${view.isBound}, playerInstance=${view.playerInstance}")
if (view.playerInstance < 0) {
return
}
// Wait for instance to be created if not yet available
val playerVideo = PlayerVideo.getInstance(view.playerInstance)
if (playerVideo == null) {
addPendingView(view)
return
}
// Remove from pending views if it was there
removePendingView(view)
val targetPlayer = if (view.isBound) playerVideo.player else null
// Always update player binding when isBound is true
if (view.isBound) {
view.player = targetPlayer
view.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
(targetPlayer as? ExoPlayer)?.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
// Set up video size callback
playerVideo.videoSizeChanged = { _, _ ->
// Refresh resize mode when video size changes
view.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
}
} else {
// Unbind player when not bound
view.player = null
}
// Try to bind any pending views now that we have more instances
if (hasPendingViews()) {
bindPendingViews()
}
}
override fun createViewInstance(reactContext: ThemedReactContext): MyPlayerView {
Log.d(TAG, "createViewInstance")
return MyPlayerView(reactContext).apply {
useController = false
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
player = null
// Disable shutter view for faster display
setShutterBackgroundColor(android.graphics.Color.TRANSPARENT)
}
}
override fun onDropViewInstance(view: MyPlayerView) {
Log.d(TAG, "onDropViewInstance")
// Remove from pending views to prevent memory leak
removePendingView(view)
// Unbind player
view.player = null
super.onDropViewInstance(view)
}
}