react-native-unified-player 0.3.4 → 0.3.5
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/android/src/main/java/com/unifiedplayer/UnifiedPlayerEventEmitter.kt +1 -1
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerModule.kt +117 -132
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerPackage.kt +2 -7
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerView.kt +11 -39
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerViewManager.kt +1 -1
- package/ios/UnifiedPlayerUIView.h +1 -0
- package/ios/UnifiedPlayerViewManager.m +16 -6
- package/package.json +1 -1
|
@@ -1,187 +1,172 @@
|
|
|
1
1
|
package com.unifiedplayer
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import com.facebook.react.bridge
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
5
|
+
import com.facebook.react.bridge.ReactMethod
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
7
|
import com.facebook.react.uimanager.UIManagerModule
|
|
8
|
-
import
|
|
9
|
-
import com.facebook.react.
|
|
10
|
-
import
|
|
8
|
+
import android.util.Log
|
|
9
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
10
|
+
import android.view.View
|
|
11
11
|
|
|
12
12
|
class UnifiedPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
companion object {
|
|
14
|
+
private const val TAG = "UnifiedPlayerModule"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
init {
|
|
18
|
+
Log.d(TAG, "UnifiedPlayerModule initialized")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override fun getName(): String {
|
|
22
|
+
Log.d(TAG, "getName() called, returning 'UnifiedPlayer'")
|
|
23
|
+
return "UnifiedPlayer"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private fun getPlayerViewByTag(viewTag: Int): UnifiedPlayerView? {
|
|
17
27
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
val view = reactApplicationContext.currentActivity?.findViewById<View>(viewTag)
|
|
29
|
+
Log.d(TAG, "Looking for view with tag: $viewTag, found: ${view != null}")
|
|
30
|
+
|
|
31
|
+
if (view is UnifiedPlayerView) {
|
|
32
|
+
return view
|
|
33
|
+
} else if (view != null) {
|
|
34
|
+
Log.e(TAG, "View with tag $viewTag is not a UnifiedPlayerView, it's a ${view.javaClass.simpleName}")
|
|
21
35
|
}
|
|
22
36
|
} catch (e: Exception) {
|
|
23
|
-
Log.e(TAG, "
|
|
37
|
+
Log.e(TAG, "Error finding view with tag $viewTag: ${e.message}", e)
|
|
24
38
|
}
|
|
39
|
+
return null
|
|
25
40
|
}
|
|
26
|
-
|
|
27
|
-
private const val TAG = "UnifiedPlayerModule"
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
override fun getName(): String = "UnifiedPlayer"
|
|
31
|
-
|
|
41
|
+
|
|
32
42
|
@ReactMethod
|
|
33
43
|
fun play(viewTag: Int, promise: Promise) {
|
|
44
|
+
Log.d(TAG, "Native play method called with viewTag: $viewTag")
|
|
34
45
|
try {
|
|
35
|
-
val
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (view != null) {
|
|
42
|
-
view.play()
|
|
46
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
47
|
+
if (playerView != null) {
|
|
48
|
+
UiThreadUtil.runOnUiThread {
|
|
49
|
+
try {
|
|
50
|
+
playerView.play()
|
|
51
|
+
Log.d(TAG, "Play command executed successfully")
|
|
43
52
|
promise.resolve(true)
|
|
44
|
-
}
|
|
45
|
-
|
|
53
|
+
} catch (e: Exception) {
|
|
54
|
+
Log.e(TAG, "Error during play: ${e.message}", e)
|
|
55
|
+
promise.reject("PLAY_ERROR", "Error during play: ${e.message}", e)
|
|
46
56
|
}
|
|
47
|
-
} catch (e: Exception) {
|
|
48
|
-
Log.e(TAG, "Error in play method", e)
|
|
49
|
-
promise.reject("PLAY_ERROR", "Error in play method", e)
|
|
50
57
|
}
|
|
51
|
-
}
|
|
58
|
+
} else {
|
|
59
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
60
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
61
|
+
}
|
|
52
62
|
} catch (e: Exception) {
|
|
53
|
-
Log.e(TAG, "Error in play method", e)
|
|
54
|
-
promise.reject("
|
|
63
|
+
Log.e(TAG, "Error in play method: ${e.message}", e)
|
|
64
|
+
promise.reject("PLAY_ERROR", "Error in play method: ${e.message}", e)
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
@ReactMethod
|
|
59
69
|
fun pause(viewTag: Int, promise: Promise) {
|
|
70
|
+
Log.d(TAG, "Native pause method called with viewTag: $viewTag")
|
|
60
71
|
try {
|
|
61
|
-
val
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (view != null) {
|
|
68
|
-
view.pause()
|
|
72
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
73
|
+
if (playerView != null) {
|
|
74
|
+
UiThreadUtil.runOnUiThread {
|
|
75
|
+
try {
|
|
76
|
+
playerView.pause()
|
|
77
|
+
Log.d(TAG, "Pause command executed successfully")
|
|
69
78
|
promise.resolve(true)
|
|
70
|
-
}
|
|
71
|
-
|
|
79
|
+
} catch (e: Exception) {
|
|
80
|
+
Log.e(TAG, "Error during pause: ${e.message}", e)
|
|
81
|
+
promise.reject("PAUSE_ERROR", "Error during pause: ${e.message}", e)
|
|
72
82
|
}
|
|
73
|
-
} catch (e: Exception) {
|
|
74
|
-
Log.e(TAG, "Error in pause method", e)
|
|
75
|
-
promise.reject("PAUSE_ERROR", "Error in pause method", e)
|
|
76
83
|
}
|
|
77
|
-
}
|
|
84
|
+
} else {
|
|
85
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
86
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
87
|
+
}
|
|
78
88
|
} catch (e: Exception) {
|
|
79
|
-
Log.e(TAG, "Error in pause method", e)
|
|
80
|
-
promise.reject("
|
|
89
|
+
Log.e(TAG, "Error in pause method: ${e.message}", e)
|
|
90
|
+
promise.reject("PAUSE_ERROR", "Error in pause method: ${e.message}", e)
|
|
81
91
|
}
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
@ReactMethod
|
|
85
95
|
fun seekTo(viewTag: Int, seconds: Float, promise: Promise) {
|
|
96
|
+
Log.d(TAG, "Native seekTo method called with viewTag: $viewTag, seconds: $seconds")
|
|
86
97
|
try {
|
|
87
|
-
val
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (view != null) {
|
|
94
|
-
view.seekTo(seconds)
|
|
98
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
99
|
+
if (playerView != null) {
|
|
100
|
+
UiThreadUtil.runOnUiThread {
|
|
101
|
+
try {
|
|
102
|
+
playerView.seekTo(seconds)
|
|
103
|
+
Log.d(TAG, "SeekTo command executed successfully to $seconds seconds")
|
|
95
104
|
promise.resolve(true)
|
|
96
|
-
}
|
|
97
|
-
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
Log.e(TAG, "Error during seekTo: ${e.message}", e)
|
|
107
|
+
promise.reject("SEEK_ERROR", "Error during seekTo: ${e.message}", e)
|
|
98
108
|
}
|
|
99
|
-
} catch (e: Exception) {
|
|
100
|
-
Log.e(TAG, "Error in seekTo method", e)
|
|
101
|
-
promise.reject("SEEK_ERROR", "Error in seekTo method", e)
|
|
102
109
|
}
|
|
103
|
-
}
|
|
110
|
+
} else {
|
|
111
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
112
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
113
|
+
}
|
|
104
114
|
} catch (e: Exception) {
|
|
105
|
-
Log.e(TAG, "Error in seekTo method", e)
|
|
106
|
-
promise.reject("
|
|
115
|
+
Log.e(TAG, "Error in seekTo method: ${e.message}", e)
|
|
116
|
+
promise.reject("SEEK_ERROR", "Error in seekTo method: ${e.message}", e)
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
@ReactMethod
|
|
111
121
|
fun getCurrentTime(viewTag: Int, promise: Promise) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
Log.d(TAG, "Native getCurrentTime method called with viewTag: $viewTag")
|
|
123
|
+
try {
|
|
124
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
125
|
+
if (playerView != null) {
|
|
126
|
+
UiThreadUtil.runOnUiThread {
|
|
127
|
+
try {
|
|
128
|
+
val currentTime = playerView.getCurrentTime()
|
|
129
|
+
Log.d(TAG, "getCurrentTime executed successfully, current time: $currentTime")
|
|
130
|
+
promise.resolve(currentTime)
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
Log.e(TAG, "Error getting current time: ${e.message}", e)
|
|
133
|
+
promise.reject("GET_TIME_ERROR", "Error getting current time: ${e.message}", e)
|
|
120
134
|
}
|
|
121
|
-
} catch (e: Exception) {
|
|
122
|
-
Log.e(TAG, "Error in getCurrentTime method", e)
|
|
123
|
-
promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method", e)
|
|
124
135
|
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
} else {
|
|
137
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
138
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
139
|
+
}
|
|
140
|
+
} catch (e: Exception) {
|
|
141
|
+
Log.e(TAG, "Error in getCurrentTime method: ${e.message}", e)
|
|
142
|
+
promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method: ${e.message}", e)
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
@ReactMethod
|
|
132
147
|
fun getDuration(viewTag: Int, promise: Promise) {
|
|
133
|
-
|
|
134
|
-
uiManager.addUIBlock(UIBlock { nativeViewHierarchyManager ->
|
|
135
|
-
try {
|
|
136
|
-
val view = nativeViewHierarchyManager.resolveView(viewTag) as? UnifiedPlayerView
|
|
137
|
-
if (view != null) {
|
|
138
|
-
promise.resolve(view.getDuration())
|
|
139
|
-
} else {
|
|
140
|
-
promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
|
|
141
|
-
}
|
|
142
|
-
} catch (e: Exception) {
|
|
143
|
-
Log.e(TAG, "Error in getDuration method", e)
|
|
144
|
-
promise.reject("GET_DURATION_ERROR", "Error in getDuration method", e)
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
} ?: run {
|
|
148
|
-
promise.reject("ERROR", "UIManagerModule not available")
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
@ReactMethod
|
|
153
|
-
fun capture(viewTag: Int, promise: Promise) {
|
|
148
|
+
Log.d(TAG, "Native getDuration method called with viewTag: $viewTag")
|
|
154
149
|
try {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
val bitmap = view.captureFrame()
|
|
166
|
-
if (bitmap != null) {
|
|
167
|
-
val outputStream = ByteArrayOutputStream()
|
|
168
|
-
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
|
|
169
|
-
val base64 = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT)
|
|
170
|
-
promise.resolve(base64)
|
|
171
|
-
} else {
|
|
172
|
-
promise.reject("CAPTURE_ERROR", "Failed to capture frame")
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
promise.reject("INVALID_VIEW", "View with tag $viewTag not found")
|
|
150
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
151
|
+
if (playerView != null) {
|
|
152
|
+
UiThreadUtil.runOnUiThread {
|
|
153
|
+
try {
|
|
154
|
+
val duration = playerView.getDuration()
|
|
155
|
+
Log.d(TAG, "getDuration executed successfully, duration: $duration")
|
|
156
|
+
promise.resolve(duration)
|
|
157
|
+
} catch (e: Exception) {
|
|
158
|
+
Log.e(TAG, "Error getting duration: ${e.message}", e)
|
|
159
|
+
promise.reject("GET_DURATION_ERROR", "Error getting duration: ${e.message}", e)
|
|
176
160
|
}
|
|
177
|
-
} catch (e: Exception) {
|
|
178
|
-
Log.e(TAG, "Error in capture method", e)
|
|
179
|
-
promise.reject("CAPTURE_ERROR", "Error in capture method", e)
|
|
180
161
|
}
|
|
181
|
-
}
|
|
162
|
+
} else {
|
|
163
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
164
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
165
|
+
}
|
|
182
166
|
} catch (e: Exception) {
|
|
183
|
-
Log.e(TAG, "Error in
|
|
184
|
-
promise.reject("
|
|
167
|
+
Log.e(TAG, "Error in getDuration method: ${e.message}", e)
|
|
168
|
+
promise.reject("GET_DURATION_ERROR", "Error in getDuration method: ${e.message}", e)
|
|
185
169
|
}
|
|
186
170
|
}
|
|
187
|
-
|
|
171
|
+
|
|
172
|
+
}
|
|
@@ -12,12 +12,7 @@ class UnifiedPlayerPackage : ReactPackage {
|
|
|
12
12
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
13
13
|
Log.d(TAG, "Creating native modules")
|
|
14
14
|
return listOf(
|
|
15
|
-
UnifiedPlayerModule(reactContext)
|
|
16
|
-
reactContext.runOnUiQueueThread {
|
|
17
|
-
// Initialize module on UI thread
|
|
18
|
-
Log.d(TAG, "Module initialized on UI thread")
|
|
19
|
-
}
|
|
20
|
-
}
|
|
15
|
+
UnifiedPlayerModule(reactContext)
|
|
21
16
|
)
|
|
22
17
|
}
|
|
23
18
|
|
|
@@ -25,4 +20,4 @@ class UnifiedPlayerPackage : ReactPackage {
|
|
|
25
20
|
Log.d(TAG, "Creating view managers")
|
|
26
21
|
return listOf(UnifiedPlayerViewManager())
|
|
27
22
|
}
|
|
28
|
-
}
|
|
23
|
+
}
|
|
@@ -4,13 +4,9 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.graphics.Color
|
|
6
6
|
import android.util.Log
|
|
7
|
-
import android.graphics.Bitmap
|
|
8
|
-
import android.graphics.Canvas
|
|
9
7
|
import android.os.Handler
|
|
10
8
|
import android.os.Looper
|
|
11
9
|
import android.view.Gravity
|
|
12
|
-
import android.view.TextureView
|
|
13
|
-
import android.view.View
|
|
14
10
|
import android.widget.FrameLayout
|
|
15
11
|
import com.facebook.react.bridge.Arguments
|
|
16
12
|
import com.google.android.exoplayer2.ExoPlayer
|
|
@@ -316,41 +312,17 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
316
312
|
}
|
|
317
313
|
}
|
|
318
314
|
|
|
319
|
-
fun getDuration(): Float {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
fun captureFrame(): Bitmap? {
|
|
332
|
-
Log.d(TAG, "CaptureFrame method called")
|
|
333
|
-
try {
|
|
334
|
-
// Create a bitmap with the same dimensions as the player view
|
|
335
|
-
val bitmap = Bitmap.createBitmap(
|
|
336
|
-
playerView.width,
|
|
337
|
-
playerView.height,
|
|
338
|
-
Bitmap.Config.ARGB_8888
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
// Create a canvas with the bitmap
|
|
342
|
-
val canvas = Canvas(bitmap)
|
|
343
|
-
|
|
344
|
-
// Draw the player view onto the canvas
|
|
345
|
-
playerView.draw(canvas)
|
|
346
|
-
|
|
347
|
-
Log.d(TAG, "Successfully captured frame from PlayerView")
|
|
348
|
-
return bitmap
|
|
349
|
-
} catch (e: Exception) {
|
|
350
|
-
Log.e(TAG, "Error capturing frame: ${e.message}", e)
|
|
351
|
-
return null
|
|
315
|
+
fun getDuration(): Float {
|
|
316
|
+
Log.d(TAG, "GetDuration method called")
|
|
317
|
+
return player?.let {
|
|
318
|
+
val duration = it.duration.toFloat() / 1000f
|
|
319
|
+
Log.d(TAG, "Duration: $duration seconds (raw: ${it.duration})")
|
|
320
|
+
if (it.duration > 0) duration else 0f
|
|
321
|
+
} ?: run {
|
|
322
|
+
Log.e(TAG, "Cannot get duration: player is null")
|
|
323
|
+
0f
|
|
324
|
+
}
|
|
352
325
|
}
|
|
353
|
-
}
|
|
354
326
|
|
|
355
327
|
// Add a getter for the ExoPlayer instance
|
|
356
328
|
val exoPlayer: ExoPlayer?
|
|
@@ -415,4 +387,4 @@ fun captureFrame(): Bitmap? {
|
|
|
415
387
|
progressHandler.removeCallbacks(progressRunnable) // Stop progress updates
|
|
416
388
|
player?.release()
|
|
417
389
|
}
|
|
418
|
-
}
|
|
390
|
+
}
|
|
@@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
16
16
|
@property (nonatomic, weak) RCTBridge *bridge;
|
|
17
17
|
@property (nonatomic, assign) VLCMediaPlayerState previousState;
|
|
18
18
|
@property (nonatomic, assign) BOOL hasRenderedVideo;
|
|
19
|
+
@property (nonatomic, assign) BOOL readyEventSent;
|
|
19
20
|
|
|
20
21
|
// Event callbacks
|
|
21
22
|
@property (nonatomic, copy) RCTDirectEventBlock onLoadStart;
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
// Initialize properties
|
|
21
21
|
_hasRenderedVideo = NO;
|
|
22
|
+
_readyEventSent = NO;
|
|
22
23
|
|
|
23
24
|
// Create the player
|
|
24
25
|
_player = [[VLCMediaPlayer alloc] init];
|
|
@@ -182,8 +183,9 @@
|
|
|
182
183
|
self.window ? @"YES" : @"NO",
|
|
183
184
|
self.superview ? @"YES" : @"NO");
|
|
184
185
|
|
|
185
|
-
// Reset the
|
|
186
|
+
// Reset the flags
|
|
186
187
|
_hasRenderedVideo = NO;
|
|
188
|
+
_readyEventSent = NO;
|
|
187
189
|
|
|
188
190
|
// Make sure we're in the main thread
|
|
189
191
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -432,6 +434,19 @@
|
|
|
432
434
|
VLCMediaPlayerState state = _player.state;
|
|
433
435
|
RCTLogInfo(@"[UnifiedPlayerViewManager] mediaPlayerStateChanged - New State: %d", state); // Added Log
|
|
434
436
|
|
|
437
|
+
// Check if media is ready to play
|
|
438
|
+
if ((state == VLCMediaPlayerStateBuffering || state == VLCMediaPlayerStatePlaying || state == VLCMediaPlayerStatePaused) && !_readyEventSent) {
|
|
439
|
+
// Check if we have video tracks
|
|
440
|
+
NSArray *videoTracks = [_player.media tracksInformation];
|
|
441
|
+
if (videoTracks.count > 0 || _player.hasVideoOut) {
|
|
442
|
+
RCTLogInfo(@"[UnifiedPlayerViewManager] Media is ready to play - Video tracks found: %lu", (unsigned long)videoTracks.count);
|
|
443
|
+
|
|
444
|
+
// Send ready event when media is ready, regardless of autoplay
|
|
445
|
+
[self sendEvent:@"onReadyToPlay" body:@{}];
|
|
446
|
+
_readyEventSent = YES;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
435
450
|
// Debug information for video output
|
|
436
451
|
if (state == VLCMediaPlayerStatePlaying) {
|
|
437
452
|
RCTLogInfo(@"[UnifiedPlayerViewManager] Video size: %@",
|
|
@@ -444,11 +459,6 @@
|
|
|
444
459
|
if (videoTracks.count > 0) {
|
|
445
460
|
RCTLogInfo(@"[UnifiedPlayerViewManager] Video tracks found: %lu", (unsigned long)videoTracks.count);
|
|
446
461
|
|
|
447
|
-
// Send ready event the first time we start playing
|
|
448
|
-
if (!_hasRenderedVideo) {
|
|
449
|
-
[self sendEvent:@"onReadyToPlay" body:@{}];
|
|
450
|
-
}
|
|
451
|
-
|
|
452
462
|
// Send playing event when we actually start playing
|
|
453
463
|
[self sendEvent:@"onPlaying" body:@{}];
|
|
454
464
|
|