react-native-unified-player 0.3.5 → 0.3.7

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.
@@ -77,6 +77,6 @@ dependencies {
77
77
  implementation "com.facebook.react:react-android"
78
78
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
79
79
 
80
- implementation "com.google.android.exoplayer:exoplayer-core:2.18.1"
81
- implementation "com.google.android.exoplayer:exoplayer-ui:2.18.1"
80
+ implementation "com.google.android.exoplayer:exoplayer-core:2.19.0"
81
+ implementation "com.google.android.exoplayer:exoplayer-ui:2.19.0"
82
82
  }
@@ -169,4 +169,30 @@ class UnifiedPlayerModule(private val reactContext: ReactApplicationContext) : R
169
169
  }
170
170
  }
171
171
 
172
- }
172
+ @ReactMethod
173
+ fun capture(viewTag: Int, promise: Promise) {
174
+ Log.d(TAG, "Native capture method called with viewTag: $viewTag")
175
+ try {
176
+ val playerView = getPlayerViewByTag(viewTag)
177
+ if (playerView != null) {
178
+ UiThreadUtil.runOnUiThread {
179
+ try {
180
+ // Assuming playerView has a method called capture() that returns a String
181
+ val captureResult = playerView.capture()
182
+ Log.d(TAG, "Capture command executed successfully, result: $captureResult")
183
+ promise.resolve(captureResult)
184
+ } catch (e: Exception) {
185
+ Log.e(TAG, "Error during capture: ${e.message}", e)
186
+ promise.reject("CAPTURE_ERROR", "Error during capture: ${e.message}", e)
187
+ }
188
+ }
189
+ } else {
190
+ Log.e(TAG, "Player view not found for tag: $viewTag")
191
+ promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
192
+ }
193
+ } catch (e: Exception) {
194
+ Log.e(TAG, "Error in capture method: ${e.message}", e)
195
+ promise.reject("CAPTURE_ERROR", "Error in capture method: ${e.message}", e)
196
+ }
197
+ }
198
+ }
@@ -2,11 +2,17 @@ package com.unifiedplayer
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.graphics.Bitmap
6
+ import android.graphics.Canvas
5
7
  import android.graphics.Color
8
+ import android.view.PixelCopy
9
+ import android.util.Base64
10
+ import java.io.ByteArrayOutputStream
6
11
  import android.util.Log
7
12
  import android.os.Handler
8
13
  import android.os.Looper
9
14
  import android.view.Gravity
15
+ import android.view.View
10
16
  import android.widget.FrameLayout
11
17
  import com.facebook.react.bridge.Arguments
12
18
  import com.google.android.exoplayer2.ExoPlayer
@@ -37,8 +43,8 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
37
43
  private var videoUrl: String? = null
38
44
  private var autoplay: Boolean = true
39
45
  private var loop: Boolean = false
40
- private var playerView: PlayerView
41
- private var player: ExoPlayer? = null
46
+ private var textureView: android.view.TextureView
47
+ internal var player: ExoPlayer? = null
42
48
  private var currentProgress = 0
43
49
  private var isPaused = false
44
50
 
@@ -76,20 +82,23 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
76
82
  // Create ExoPlayer
77
83
  player = ExoPlayer.Builder(context).build()
78
84
 
79
- // Create PlayerView
80
- playerView = PlayerView(context).apply {
81
- layoutParams = LayoutParams(
82
- LayoutParams.MATCH_PARENT,
83
- LayoutParams.MATCH_PARENT
84
- )
85
- setPlayer(player)
86
- // playerView.surfaceView?.surfaceType = android.view.SurfaceView.SURFACE_TYPE_SOFTWARE // Reverted: Let's remove surfaceType setting
87
- }
85
+ // Create TextureView for video rendering
86
+ textureView = android.view.TextureView(context).apply {
87
+ layoutParams = LayoutParams(
88
+ LayoutParams.MATCH_PARENT,
89
+ LayoutParams.MATCH_PARENT
90
+ )
91
+ }
92
+
93
+ // Add TextureView to the view hierarchy
94
+ addView(textureView)
88
95
 
89
- addView(playerView)
90
- // Add logging for playerView dimensions and post play call
91
- playerView.post {
92
- Log.d(TAG, "PlayerView dimensions after addView: width=${playerView.width}, height=${playerView.height}")
96
+ // We'll set the video surface when the TextureView's surface is available
97
+ // in the onSurfaceTextureAvailable callback
98
+ Log.d(TAG, "TextureView added to view hierarchy")
99
+ // Log dimensions of the view
100
+ post {
101
+ Log.d(TAG, "UnifiedPlayerView dimensions after init: width=${width}, height=${height}")
93
102
  }
94
103
 
95
104
  player?.addListener(object : Player.Listener {
@@ -175,24 +184,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
175
184
  }
176
185
 
177
186
  override fun onVideoSizeChanged(videoSize: VideoSize) {
178
- // Called when video size changes.
179
- Log.d(TAG, "ExoPlayer onVideoSizeChanged: videoSize=$videoSize")
180
- Log.d(TAG, "Video size changed: width=${videoSize.width}, height=${videoSize.height}")
181
- }
182
-
183
- override fun onSurfaceSizeChanged(width: Int, height: Int) {
184
- // Called when the size of the surface changes.
185
- Log.d(TAG, "ExoPlayer onSurfaceSizeChanged: width=$width, height=$height")
186
- }
187
-
188
- override fun onRenderedFirstFrame() {
189
- // Called when the first frame is rendered.
190
- Log.d(TAG, "ExoPlayer onRenderedFirstFrame")
191
- }
192
-
193
- override fun onSkipSilenceEnabledChanged(skipSilenceEnabled: Boolean) {
194
- // Called when skip silence is enabled or disabled.
195
- Log.d(TAG, "ExoPlayer onSkipSilenceEnabledChanged: skipSilenceEnabled=$skipSilenceEnabled")
187
+ // Handle video size changes if needed
196
188
  }
197
189
  })
198
190
  }
@@ -253,14 +245,9 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
253
245
  }
254
246
  }
255
247
 
256
- fun setAuthToken(token: String?) {
257
- // Removed as per request
258
- Log.d(TAG, "Auth token handling removed")
259
- }
260
-
261
248
  fun setAutoplay(value: Boolean) {
262
249
  autoplay = value
263
- player?.playWhenReady = value // Set ExoPlayer's playWhenReady property
250
+ player?.playWhenReady = value
264
251
  }
265
252
 
266
253
  fun setLoop(value: Boolean) {
@@ -296,7 +283,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
296
283
  it.seekTo(milliseconds)
297
284
 
298
285
  // Force a progress update after seeking
299
- progressRunnable.run()
286
+ progressRunnable.run()
300
287
  } ?: Log.e(TAG, "Cannot seek: player is null")
301
288
  }
302
289
 
@@ -370,14 +357,39 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
370
357
  val width = right - left
371
358
  val height = bottom - top
372
359
  Log.d(TAG, "UnifiedPlayerView onLayout: width=$width, height=$height")
373
- // Ensure playerView also gets laid out
374
- playerView.layout(0, 0, width, height)
360
+ // Ensure textureView gets laid out properly
361
+ textureView.layout(0, 0, width, height)
375
362
  }
376
363
 
377
364
  override fun onAttachedToWindow() {
378
365
  super.onAttachedToWindow()
379
366
  Log.d(TAG, "UnifiedPlayerView onAttachedToWindow")
380
- playerView.setPlayer(player)
367
+ textureView.surfaceTextureListener = object : android.view.TextureView.SurfaceTextureListener {
368
+ override fun onSurfaceTextureAvailable(surface: android.graphics.SurfaceTexture, width: Int, height: Int) {
369
+ Log.d(TAG, "TextureView onSurfaceTextureAvailable: width=$width, height=$height")
370
+ // Create a Surface from the SurfaceTexture and set it on the player
371
+ val videoSurface = android.view.Surface(surface)
372
+ player?.setVideoSurface(videoSurface)
373
+ Log.d(TAG, "Set video surface from TextureView's SurfaceTexture")
374
+ }
375
+
376
+ override fun onSurfaceTextureSizeChanged(surface: android.graphics.SurfaceTexture, width: Int, height: Int) {
377
+ Log.d(TAG, "TextureView onSurfaceTextureSizeChanged: width=$width, height=$height")
378
+ }
379
+
380
+ override fun onSurfaceTextureDestroyed(surface: android.graphics.SurfaceTexture): Boolean {
381
+ Log.d(TAG, "TextureView onSurfaceTextureDestroyed")
382
+ // Set the player's surface to null to release it
383
+ player?.setVideoSurface(null)
384
+ Log.d(TAG, "Cleared video surface from player")
385
+ return true
386
+ }
387
+
388
+ override fun onSurfaceTextureUpdated(surface: android.graphics.SurfaceTexture) {
389
+ // This is called very frequently, so we'll comment out this log
390
+ // Log.d(TAG, "TextureView onSurfaceTextureUpdated")
391
+ }
392
+ }
381
393
  startProgressUpdates() // Use the new method to start progress updates
382
394
  }
383
395
 
@@ -387,4 +399,62 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
387
399
  progressHandler.removeCallbacks(progressRunnable) // Stop progress updates
388
400
  player?.release()
389
401
  }
390
- }
402
+
403
+ fun capture(): String {
404
+ Log.d(TAG, "Capture method called")
405
+ return try {
406
+ player?.let { exoPlayer ->
407
+ // Get the video size from the player
408
+ val videoSize = exoPlayer.videoSize
409
+ if (videoSize.width <= 0 || videoSize.height <= 0) {
410
+ Log.e(TAG, "Invalid video dimensions: ${videoSize.width}x${videoSize.height}")
411
+ return ""
412
+ }
413
+
414
+ // Get bitmap directly from TextureView
415
+ val bitmap = textureView.bitmap ?: run {
416
+ Log.e(TAG, "Failed to get bitmap from TextureView")
417
+ return ""
418
+ }
419
+
420
+ // Debugging: Log the dimensions of the bitmap
421
+ bitmap.let {
422
+ Log.d(TAG, "Bitmap dimensions: width=${it.width}, height=${it.height}")
423
+
424
+ // Check if bitmap is empty (all black)
425
+ var hasNonBlackPixel = false
426
+ for (x in 0 until it.width) {
427
+ for (y in 0 until it.height) {
428
+ if (it.getPixel(x, y) != Color.BLACK) {
429
+ hasNonBlackPixel = true
430
+ break
431
+ }
432
+ }
433
+ if (hasNonBlackPixel) break
434
+ }
435
+
436
+ if (!hasNonBlackPixel) {
437
+ Log.w(TAG, "Bitmap appears to be all black")
438
+ }
439
+ }
440
+ // Compress and encode the bitmap
441
+ val byteArrayOutputStream = ByteArrayOutputStream()
442
+ if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)) {
443
+ val byteArray = byteArrayOutputStream.toByteArray()
444
+ val base64EncodedString = Base64.encodeToString(byteArray, Base64.DEFAULT)
445
+ Log.d(TAG, "Capture successful, base64 length: ${base64EncodedString.length}")
446
+ base64EncodedString
447
+ } else {
448
+ Log.e(TAG, "Failed to compress bitmap")
449
+ ""
450
+ }
451
+ } ?: run {
452
+ Log.e(TAG, "Cannot capture: player is null")
453
+ ""
454
+ }
455
+ } catch (e: Exception) {
456
+ Log.e(TAG, "Error during capture: ${e.message}", e)
457
+ ""
458
+ }
459
+ }
460
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-unified-player",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Unified Player",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",