[Video] Manage foreground/background playback on the native side (#5104)

zio/stable
Hailey 2024-09-03 08:41:14 -07:00 committed by GitHub
parent 05e61346b8
commit dde72b48e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 104 additions and 29 deletions

View File

@ -80,6 +80,57 @@ index 0000000..0249e23
+ } + }
+} +}
\ No newline at end of file \ No newline at end of file
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
index 4b6c6d8..e20f51a 100644
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
@@ -1,5 +1,6 @@
package expo.modules.video
+import android.provider.MediaStore.Video
import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi
import expo.modules.kotlin.AppContext
@@ -15,6 +16,8 @@ object VideoManager {
// Keeps track of all existing VideoPlayers, and whether they are attached to a VideoView
private var videoPlayersToVideoViews = mutableMapOf<VideoPlayer, MutableList<VideoView>>()
+ private var previouslyPlayingViews: MutableList<VideoView>? = null
+
private lateinit var audioFocusManager: AudioFocusManager
fun onModuleCreated(appContext: AppContext) {
@@ -69,16 +72,24 @@ object VideoManager {
return videoPlayersToVideoViews[videoPlayer]?.isNotEmpty() ?: false
}
- fun onAppForegrounded() = Unit
+ fun onAppForegrounded() {
+ val previouslyPlayingViews = this.previouslyPlayingViews ?: return
+ for (videoView in previouslyPlayingViews) {
+ val player = videoView.videoPlayer?.player ?: continue
+ player.play()
+ }
+ this.previouslyPlayingViews = null
+ }
fun onAppBackgrounded() {
+ val previouslyPlayingViews = mutableListOf<VideoView>()
for (videoView in videoViews.values) {
- if (videoView.videoPlayer?.staysActiveInBackground == false &&
- !videoView.willEnterPiP &&
- !videoView.isInFullscreen
- ) {
- videoView.videoPlayer?.player?.pause()
+ val player = videoView.videoPlayer?.player ?: continue
+ if (player.isPlaying) {
+ player.pause()
+ previouslyPlayingViews.add(videoView)
}
}
+ this.previouslyPlayingViews = previouslyPlayingViews
}
}
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
index ec3da2a..5a1397a 100644 index ec3da2a..5a1397a 100644
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt --- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
@ -220,12 +271,48 @@ index cb9ca6d..ed8bb7e 100644
//# sourceMappingURL=VideoView.types.d.ts.map //# sourceMappingURL=VideoView.types.d.ts.map
\ No newline at end of file \ No newline at end of file
diff --git a/node_modules/expo-video/ios/VideoManager.swift b/node_modules/expo-video/ios/VideoManager.swift diff --git a/node_modules/expo-video/ios/VideoManager.swift b/node_modules/expo-video/ios/VideoManager.swift
index 094a8b0..412fd0c 100644 index 094a8b0..3f00525 100644
--- a/node_modules/expo-video/ios/VideoManager.swift --- a/node_modules/expo-video/ios/VideoManager.swift
+++ b/node_modules/expo-video/ios/VideoManager.swift +++ b/node_modules/expo-video/ios/VideoManager.swift
@@ -51,45 +51,45 @@ class VideoManager { @@ -12,6 +12,7 @@ class VideoManager {
private var videoViews = NSHashTable<VideoView>.weakObjects()
private var videoPlayers = NSHashTable<VideoPlayer>.weakObjects()
+ private var previouslyPlayingPlayers: [VideoPlayer]?
func register(videoPlayer: VideoPlayer) {
videoPlayers.add(videoPlayer)
@@ -33,63 +34,70 @@ class VideoManager {
for videoPlayer in videoPlayers.allObjects {
videoPlayer.setTracksEnabled(true)
}
+
+ if let previouslyPlayingPlayers = self.previouslyPlayingPlayers {
+ previouslyPlayingPlayers.forEach { player in
+ player.pointer.play()
+ }
+ }
}
func onAppBackgrounded() {
+ var previouslyPlayingPlayers: [VideoPlayer] = []
for videoView in videoViews.allObjects {
guard let player = videoView.player else {
continue
}
- if player.staysActiveInBackground == true {
- player.setTracksEnabled(videoView.isInPictureInPicture)
- } else if !videoView.isInPictureInPicture {
+ if player.isPlaying {
player.pointer.pause()
+ previouslyPlayingPlayers.append(player)
}
}
+ self.previouslyPlayingPlayers = previouslyPlayingPlayers
}
// MARK: - Audio Session Management // MARK: - Audio Session Management
internal func setAppropriateAudioSessionOrWarn() { internal func setAppropriateAudioSessionOrWarn() {
- let audioSession = AVAudioSession.sharedInstance() - let audioSession = AVAudioSession.sharedInstance()
- var audioSessionCategoryOptions: AVAudioSession.CategoryOptions = [] - var audioSessionCategoryOptions: AVAudioSession.CategoryOptions = []

View File

@ -2,8 +2,17 @@
## `expo-video` Patch ## `expo-video` Patch
This patch adds two props to `VideoView`: `onEnterFullscreen` and `onExitFullscreen` which do exactly what they say on ### `onEnterFullScreen`/`onExitFullScreen`
Adds two props to `VideoView`: `onEnterFullscreen` and `onExitFullscreen` which do exactly what they say on
the tin. the tin.
### Removing audio session management
This patch also removes the audio session management that Expo does on its own, as we handle audio session management This patch also removes the audio session management that Expo does on its own, as we handle audio session management
ourselves. ourselves.
### Pausing/playing on background/foreground
Instead of handling the pausing/playing of videos in React, we'll handle them here. There's some logic that we do not
need (around PIP mode) that we can remove, and just pause any playing players on background and then resume them on
foreground.

View File

@ -5,12 +5,9 @@ import {VideoPlayer, VideoView} from 'expo-video'
import {AppBskyEmbedVideo} from '@atproto/api' import {AppBskyEmbedVideo} from '@atproto/api'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useIsFocused} from '@react-navigation/native'
import {HITSLOP_30} from '#/lib/constants' import {HITSLOP_30} from '#/lib/constants'
import {useAppState} from '#/lib/hooks/useAppState'
import {clamp} from '#/lib/numbers' import {clamp} from '#/lib/numbers'
import {logger} from '#/logger'
import {useActiveVideoNative} from 'view/com/util/post-embeds/ActiveVideoNativeContext' import {useActiveVideoNative} from 'view/com/util/post-embeds/ActiveVideoNativeContext'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
@ -29,26 +26,6 @@ export function VideoEmbedInnerNative({
const {_} = useLingui() const {_} = useLingui()
const {player} = useActiveVideoNative() const {player} = useActiveVideoNative()
const ref = useRef<VideoView>(null) const ref = useRef<VideoView>(null)
const isScreenFocused = useIsFocused()
const isAppFocused = useAppState()
useEffect(() => {
try {
if (isAppFocused === 'active' && isScreenFocused && !player.playing) {
PlatformInfo.setAudioCategory(AudioCategory.Ambient)
PlatformInfo.setAudioActive(false)
player.muted = true
player.play()
} else if (player.playing) {
player.pause()
}
} catch (err) {
logger.error(
'Failed to play/pause while backgrounding/switching screens',
{safeMessage: err},
)
}
}, [isAppFocused, player, isScreenFocused])
const enterFullscreen = useCallback(() => { const enterFullscreen = useCallback(() => {
ref.current?.enterFullscreen() ref.current?.enterFullscreen()
@ -69,7 +46,7 @@ export function VideoEmbedInnerNative({
player={player} player={player}
style={[a.flex_1, a.rounded_sm]} style={[a.flex_1, a.rounded_sm]}
contentFit="contain" contentFit="contain"
nativeControls={true} nativeControls={false}
accessibilityIgnoresInvertColors accessibilityIgnoresInvertColors
onEnterFullscreen={() => { onEnterFullscreen={() => {
PlatformInfo.setAudioCategory(AudioCategory.Playback) PlatformInfo.setAudioCategory(AudioCategory.Playback)
@ -80,7 +57,9 @@ export function VideoEmbedInnerNative({
PlatformInfo.setAudioCategory(AudioCategory.Ambient) PlatformInfo.setAudioCategory(AudioCategory.Ambient)
PlatformInfo.setAudioActive(false) PlatformInfo.setAudioActive(false)
player.muted = true player.muted = true
if (!player.playing) player.play() if (!player.playing) {
player.play()
}
}} }}
accessibilityLabel={ accessibilityLabel={
embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`) embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`)