mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-05 05:36:17 +00:00
fix(pip): fix black screen
This commit is contained in:
@@ -8,7 +8,6 @@ import android.app.PendingIntent
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.SurfaceHolder
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.media3.common.AudioAttributes
|
import androidx.media3.common.AudioAttributes
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
@@ -208,16 +207,29 @@ class OmniPlayer(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSurface(holder: SurfaceHolder?) {
|
fun setVideoView(surfaceView: android.view.SurfaceView?) {
|
||||||
runOnMainThread {
|
runOnMainThread {
|
||||||
if (holder == null) {
|
if (surfaceView == null) {
|
||||||
localPlayer.clearVideoSurface()
|
localPlayer.clearVideoSurface()
|
||||||
} else {
|
} else {
|
||||||
localPlayer.setVideoSurfaceHolder(holder)
|
localPlayer.setVideoSurfaceView(surfaceView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rebuild video pipeline after pip, noop for exoplayer that recover on it's own.
|
||||||
|
fun rebuildVideoOutput() {
|
||||||
|
runOnMainThread {
|
||||||
|
(localPlayer as? VlcPlayer)?.rebuildVideoOutput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateVideoLayout(width: Int, height: Int) {
|
||||||
|
runOnMainThread {
|
||||||
|
(localPlayer as? VlcPlayer)?.updateVideoLayout(width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val hasPrev: Boolean get() = player.hasPreviousMediaItem()
|
override val hasPrev: Boolean get() = player.hasPreviousMediaItem()
|
||||||
override val hasNext: Boolean get() = player.hasNextMediaItem()
|
override val hasNext: Boolean get() = player.hasNextMediaItem()
|
||||||
override val status by mainThreadProperty {
|
override val status by mainThreadProperty {
|
||||||
|
|||||||
@@ -140,12 +140,12 @@ class OmniView(val context: ThemedReactContext) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
boundPlayer?.localPlayer?.removeListener(this)
|
boundPlayer?.localPlayer?.removeListener(this)
|
||||||
boundPlayer?.setSurface(null)
|
boundPlayer?.setVideoView(null)
|
||||||
boundPlayer = omniPlayer
|
boundPlayer = omniPlayer
|
||||||
omniPlayer.localPlayer.addListener(this)
|
omniPlayer.localPlayer.addListener(this)
|
||||||
|
|
||||||
if (surfaceReady) {
|
if (surfaceReady) {
|
||||||
omniPlayer.setSurface(surfaceView.holder)
|
omniPlayer.setVideoView(surfaceView)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoplay == true && !omniPlayer.isPlaying) {
|
if (autoplay == true && !omniPlayer.isPlaying) {
|
||||||
@@ -166,7 +166,7 @@ class OmniView(val context: ThemedReactContext) :
|
|||||||
|
|
||||||
val omniPlayer = player as? OmniPlayer ?: return
|
val omniPlayer = player as? OmniPlayer ?: return
|
||||||
boundPlayer?.localPlayer?.removeListener(this)
|
boundPlayer?.localPlayer?.removeListener(this)
|
||||||
omniPlayer.setSurface(null)
|
omniPlayer.setVideoView(null)
|
||||||
boundPlayer = null
|
boundPlayer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,9 +322,22 @@ class OmniView(val context: ThemedReactContext) :
|
|||||||
movedSurfaceToRootForPip = false
|
movedSurfaceToRootForPip = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A surface that is destroyed and recreated while playback runs (the PIP
|
||||||
|
// reparent) makes VLC drop its video pipeline; it won't rebuild it for the
|
||||||
|
// new surface on its own, so we must ask it to. The rebuild only succeeds
|
||||||
|
// once the (PIP) window has settled at its final size — firing it mid-resize
|
||||||
|
// leaves the vout stopped — so it is debounced off surfaceChanged.
|
||||||
|
private var pendingVideoRebuild = false
|
||||||
|
private val rebuildRunnable = Runnable {
|
||||||
|
if (!pendingVideoRebuild) return@Runnable
|
||||||
|
pendingVideoRebuild = false
|
||||||
|
boundPlayer?.rebuildVideoOutput()
|
||||||
|
}
|
||||||
|
|
||||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||||
surfaceReady = true
|
surfaceReady = true
|
||||||
boundPlayer?.setSurface(holder)
|
boundPlayer?.setVideoView(surfaceView)
|
||||||
|
if (boundPlayer?.isPlaying == true) pendingVideoRebuild = true
|
||||||
updatePictureInPictureParams()
|
updatePictureInPictureParams()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,10 +346,17 @@ class OmniView(val context: ThemedReactContext) :
|
|||||||
format: Int,
|
format: Int,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int
|
height: Int
|
||||||
) { }
|
) {
|
||||||
|
boundPlayer?.updateVideoLayout(width, height)
|
||||||
|
if (pendingVideoRebuild) {
|
||||||
|
surfaceView.removeCallbacks(rebuildRunnable)
|
||||||
|
surfaceView.postDelayed(rebuildRunnable, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||||
surfaceReady = false
|
surfaceReady = false
|
||||||
boundPlayer?.setSurface(null)
|
surfaceView.removeCallbacks(rebuildRunnable)
|
||||||
|
boundPlayer?.setVideoView(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ class VlcPlayer(ctx: Context) :
|
|||||||
private var playlistMetadata: MediaMetadata = MediaMetadata.EMPTY
|
private var playlistMetadata: MediaMetadata = MediaMetadata.EMPTY
|
||||||
private var userInitiatedTransition: Boolean = false
|
private var userInitiatedTransition: Boolean = false
|
||||||
private var cachedBufferedPosition: Long = 0L
|
private var cachedBufferedPosition: Long = 0L
|
||||||
|
private var boundSurfaceView: SurfaceView? = null
|
||||||
|
|
||||||
private val availableCommands: Player.Commands = Player.Commands.Builder()
|
private val availableCommands: Player.Commands = Player.Commands.Builder()
|
||||||
.add(COMMAND_PLAY_PAUSE)
|
.add(COMMAND_PLAY_PAUSE)
|
||||||
@@ -795,6 +796,7 @@ class VlcPlayer(ctx: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun clearVideoSurface() {
|
override fun clearVideoSurface() {
|
||||||
|
boundSurfaceView = null
|
||||||
vlcVout.detachViews()
|
vlcVout.detachViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,10 +827,39 @@ class VlcPlayer(ctx: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setVideoSurfaceView(surfaceView: SurfaceView?) {
|
override fun setVideoSurfaceView(surfaceView: SurfaceView?) {
|
||||||
vlcVout.setVideoView(surfaceView ?: return clearVideoSurface())
|
if (surfaceView == null) return clearVideoSurface()
|
||||||
|
if (vlcVout.areViewsAttached() && boundSurfaceView === surfaceView) return
|
||||||
|
boundSurfaceView = surfaceView
|
||||||
|
// Hand VLC the SurfaceView (not a raw Surface) so it reads the real view
|
||||||
|
// size and keeps the video layout correct as the surface resizes (e.g. the
|
||||||
|
// PIP window shrinking/growing); see updateVideoLayout for size updates.
|
||||||
|
vlcVout.setVideoView(surfaceView)
|
||||||
vlcVout.attachViews()
|
vlcVout.attachViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VLC tears down its video decoder + output whenever the output Surface is
|
||||||
|
* destroyed (e.g. the SurfaceView being reparented for PIP) and does not
|
||||||
|
* rebuild them when a new Surface arrives — the picture stays black. Toggling
|
||||||
|
* the video track forces VLC to spin up a fresh decoder/vout against the
|
||||||
|
* currently-attached Surface. Safe no-op when nothing is attached/playing.
|
||||||
|
*/
|
||||||
|
fun rebuildVideoOutput() {
|
||||||
|
if (!vlcVout.areViewsAttached() || !player.isPlaying) return
|
||||||
|
player.setVideoTrackEnabled(false)
|
||||||
|
player.setVideoTrackEnabled(true)
|
||||||
|
player.updateVideoSurfaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Recompute the video layout for the current surface size. Must run whenever
|
||||||
|
* the surface resizes (VLC latches a stale geometry otherwise -> the picture
|
||||||
|
* renders at the wrong size, anchored in a corner). */
|
||||||
|
fun updateVideoLayout(width: Int, height: Int) {
|
||||||
|
if (!vlcVout.areViewsAttached()) return
|
||||||
|
if (width > 0 && height > 0) vlcVout.setWindowSize(width, height)
|
||||||
|
player.updateVideoSurfaces()
|
||||||
|
}
|
||||||
|
|
||||||
override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
|
override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
|
||||||
clearVideoSurface()
|
clearVideoSurface()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user