File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -86,6 +86,18 @@ object PlayerCore {
8686 private var player: ExoPlayer ? = null
8787 private var playerView: PlayerView ? = null
8888
89+ /* *
90+ * Surface type for the singleton PlayerView, set via [setUseTextureView]
91+ * before [initialize] first inflates it. surface_type is only settable via
92+ * layout attrs, hence the two layouts. Changing this after the view exists
93+ * has no effect until the app restarts.
94+ */
95+ private var useTextureView = true
96+
97+ fun setUseTextureView (value : Boolean ) {
98+ useTextureView = value
99+ }
100+
89101 // Second engine: a re-parentable WebView running the YouTube IFrame API.
90102 private enum class Engine { EXO , WEB }
91103 private var engine = Engine .EXO
@@ -171,8 +183,10 @@ object PlayerCore {
171183 player = exo
172184
173185 // Inflated from XML because surface_type can only be set via attrs.
186+ val layoutRes =
187+ if (useTextureView) R .layout.video_player_view else R .layout.video_player_view_surface
174188 val view = LayoutInflater .from(context.applicationContext)
175- .inflate(R .layout.video_player_view , null ) as PlayerView
189+ .inflate(layoutRes , null ) as PlayerView
176190 view.player = exo
177191 playerView = view
178192 }
Original file line number Diff line number Diff line change @@ -35,6 +35,10 @@ class VideoModule(reactContext: ReactApplicationContext) :
3535 }
3636 }
3737
38+ override fun setUseTextureView (useTextureView : Boolean ) {
39+ PlayerCore .setUseTextureView(useTextureView)
40+ }
41+
3842 override fun releasePlayer () {
3943 UiThreadUtil .runOnUiThread {
4044 PlayerCore .release()
Original file line number Diff line number Diff line change 1+ <?xml version =" 1.0" encoding =" utf-8" ?>
2+ <!-- SurfaceView variant, opted into via setUseTextureView(false). Cheaper to
3+ render than a TextureView but can't be transformed/animated and tends to
4+ black-flash across a re-parent; only used when the app asks for it. -->
5+ <androidx .media3.ui.PlayerView
6+ xmlns : android =" http://schemas.android.com/apk/res/android"
7+ xmlns : app =" http://schemas.android.com/apk/res-auto"
8+ android : layout_width =" match_parent"
9+ android : layout_height =" match_parent"
10+ app : surface_type =" surface_view"
11+ app : use_controller =" false" />
Original file line number Diff line number Diff line change @@ -69,6 +69,12 @@ - (void)releasePlayer
6969 });
7070}
7171
72+ - (void )setUseTextureView : (BOOL )useTextureView
73+ {
74+ // TextureView vs. SurfaceView is an Android-only concept (AVPlayerLayer
75+ // has no equivalent choice) — nothing to do on iOS.
76+ }
77+
7278#pragma mark - Source
7379
7480- (void )setSource : (NativeVideoSource &)source autoplay : (BOOL )autoplay
Original file line number Diff line number Diff line change 1616const force =
1717 process . argv . includes ( '--force' ) || process . env . AU_VIDEO_BANNER === '1' ;
1818
19- const BANNER = `
20- ________ .___ _________.__ .__
21- / _____/ __ _________ __| _/____ ____ ______ / _____/|__| ____ ____ | |__
22- / \ ___| | \_ __ \/ __ |/ __ \_/ __ \\____ \ \_____ \ | |/ \ / ___\| | \
23- \ \_\ \ | /| | \/ /_/ \ ___/\ ___/| |_> > / \| | | \/ /_/ > Y \
24- \______ /____/ |__| \____ |\___ >\___ > __/ /_______ /|__|___| /\___ /|___| /
25- \/ \/ \/ \/|__| \/ \//_____/ \/
26- ` ;
19+ const BANNER = String . raw `
20+ __ __ ______ _______ ___ __ _ _______ __ __
21+ | |_| || _ | | || | | | | || || | | |
22+ | || | || | _____|| | | |_| || ___|| |_| |
23+ | || |_||_ | |_____ | | | || | __ | |
24+ | || __ | |_____ || | | _ || || || |
25+ | ||_|| || | | | _____| || | | | | || |_| || _ |
26+ |_| |_||___| |_| |_______||___| |_| |__||_______||__| |__|
27+ ` ;
2728
2829try {
2930 const quiet = process . env . CI || process . env . npm_config_loglevel === 'silent' ;
Original file line number Diff line number Diff line change @@ -64,6 +64,16 @@ export interface Spec extends TurboModule {
6464 /** Idempotent. Creates the singleton native player if needed. */
6565 nativeInit ( ) : void ;
6666
67+ /**
68+ * Android only (no-op on iOS): back the player view with a TextureView
69+ * (default) or a SurfaceView. Must be called before `nativeInit()` creates
70+ * the singleton player view — later calls are ignored until the app
71+ * restarts. TextureView re-parents cleanly across surfaces (floating
72+ * window, feed cells) at a small performance cost; SurfaceView is cheaper
73+ * but can't be animated/transformed and misbehaves when re-parented.
74+ */
75+ setUseTextureView ( useTextureView : boolean ) : void ;
76+
6777 /**
6878 * Load a source into the engine. If the currently loaded source has the
6979 * same `id`, this is a no-op (same-video handoff) and playback continues.
Original file line number Diff line number Diff line change @@ -154,6 +154,7 @@ export class VideoManager {
154154 liveAutoRetry : true ,
155155 resumeOnFocus : true ,
156156 debug : false ,
157+ useTextureView : true ,
157158 } ;
158159 /** Last non-reserved surface, restored after fullscreen/floating exits. */
159160 private lastInlineSurfaceId : string | null = null ;
@@ -271,6 +272,9 @@ export class VideoManager {
271272 return ;
272273 }
273274 this . initialized = true ;
275+ // Must precede nativeInit(): it decides which surface type Android
276+ // inflates the singleton PlayerView with, and that only happens once.
277+ NativeVideo . setUseTextureView ( this . config . useTextureView ) ;
274278 NativeVideo . nativeInit ( ) ;
275279 this . subscribeNative ( ) ;
276280 this . setupNetInfo ( ) ;
Original file line number Diff line number Diff line change @@ -168,4 +168,17 @@ export interface VideoProviderConfig {
168168 * invisible from the outside. Off by default.
169169 */
170170 debug ?: boolean ;
171+ /**
172+ * Android only (ignored on iOS). Back the player view with a TextureView
173+ * (default) or a SurfaceView.
174+ *
175+ * TextureView is what makes the player re-parent cleanly between surfaces
176+ * (inline -> fullscreen -> floating, feed cells) without a black flash, at
177+ * a small rendering-performance cost. SurfaceView is cheaper but can't be
178+ * animated or transformed and tends to show a black frame across a
179+ * re-parent — only worth it if the app never moves the player between
180+ * surfaces. Applied once, at provider init; changing it later has no
181+ * effect until the app restarts. Default true.
182+ */
183+ useTextureView ?: boolean ;
171184}
You can’t perform that action at this time.
0 commit comments