diff --git a/package/expo-package/src/optionalDependencies/AudioVideo.ts b/package/expo-package/src/optionalDependencies/AudioVideo.ts index 486a47842f..b023130bf8 100644 --- a/package/expo-package/src/optionalDependencies/AudioVideo.ts +++ b/package/expo-package/src/optionalDependencies/AudioVideo.ts @@ -1,17 +1,20 @@ let AudioComponent; +let VideoComponent; let RecordingObject; + try { const audioVideoPackage = require('expo-av'); AudioComponent = audioVideoPackage.Audio; + VideoComponent = audioVideoPackage.Video; RecordingObject = audioVideoPackage.RecordingObject; } catch (e) { // do nothing } -if (!AudioComponent) { +if (!AudioComponent || !VideoComponent) { console.log( 'Audio Video library is currently not installed. To allow in-app audio playback, install the "expo-av" package.', ); } -export { AudioComponent, RecordingObject }; +export { AudioComponent, RecordingObject, VideoComponent }; diff --git a/package/expo-package/src/optionalDependencies/Video.tsx b/package/expo-package/src/optionalDependencies/Video.tsx index 28876d528d..3cba7d794e 100644 --- a/package/expo-package/src/optionalDependencies/Video.tsx +++ b/package/expo-package/src/optionalDependencies/Video.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { useEventListener } from 'expo'; -import { AudioComponent } from './AudioVideo'; +import { AudioComponent, VideoComponent as ExpoAVVideoComponent } from './AudioVideo'; let videoPackage; @@ -18,56 +18,98 @@ if (!videoPackage) { ); } -const VideoComponent = videoPackage ? videoPackage.VideoView : null; +const VideoComponent = videoPackage ? videoPackage.VideoView : ExpoAVVideoComponent; const useVideoPlayer = videoPackage ? videoPackage.useVideoPlayer : null; -export const Video = videoPackage - ? ({ onLoadStart, onLoad, onEnd, onProgress, onBuffer, resizeMode, style, uri, videoRef }) => { - const player = useVideoPlayer(uri, (player) => { - player.timeUpdateEventInterval = 0.5; - videoRef.current = player; - }); +let Video = null; - useEventListener(player, 'statusChange', ({ status, oldStatus }) => { - if ((!oldStatus || oldStatus === 'idle') && status === 'loading') { - onLoadStart(); - } else if ((oldStatus === 'loading' || oldStatus === 'idle') && status === 'readyToPlay') { - onLoad({ duration: player.duration }); - onBuffer({ buffering: false }); - } else if (oldStatus === 'readyToPlay' && status === 'loading') { - onBuffer({ buffering: true }); - } - }); +// expo-video +if (videoPackage) { + Video = ({ + onLoadStart, + onLoad, + onEnd, + onProgress, + onBuffer, + resizeMode, + style, + uri, + videoRef, + }) => { + const player = useVideoPlayer(uri, (player) => { + player.timeUpdateEventInterval = 0.5; + videoRef.current = player; + }); + + useEventListener(player, 'statusChange', ({ status, oldStatus }) => { + if ((!oldStatus || oldStatus === 'idle') && status === 'loading') { + onLoadStart(); + } else if ((oldStatus === 'loading' || oldStatus === 'idle') && status === 'readyToPlay') { + onLoad({ duration: player.duration }); + onBuffer({ buffering: false }); + } else if (oldStatus === 'readyToPlay' && status === 'loading') { + onBuffer({ buffering: true }); + } + }); - useEventListener(player, 'playToEnd', () => { - player.replay(); - onEnd(); - }); + useEventListener(player, 'playToEnd', () => { + player.replay(); + onEnd(); + }); - useEventListener(player, 'timeUpdate', ({ currentTime }) => - onProgress({ currentTime, seekableDuration: player.duration }), - ); + useEventListener(player, 'timeUpdate', ({ currentTime }) => + onProgress({ currentTime, seekableDuration: player.duration }), + ); + + // This is done so that the audio of the video is not muted when the phone is in silent mode for iOS. + useEffect(() => { + const initializeSound = async () => { + if (AudioComponent) { + await AudioComponent.setAudioModeAsync({ + playsInSilentModeIOS: true, + }); + } + }; + initializeSound(); + }, []); - // This is done so that the audio of the video is not muted when the phone is in silent mode for iOS. - useEffect(() => { - const initializeSound = async () => { - if (AudioComponent) { - await AudioComponent.setAudioModeAsync({ - playsInSilentModeIOS: true, - }); - } - }; - initializeSound(); - }, []); + return ( + + ); + }; +} +// expo-av +else if (ExpoAVVideoComponent) { + Video = ({ onPlaybackStatusUpdate, paused, resizeMode, style, uri, videoRef }) => { + // This is done so that the audio of the video is not muted when the phone is in silent mode for iOS. + useEffect(() => { + const initializeSound = async () => { + await AudioComponent.setAudioModeAsync({ + playsInSilentModeIOS: true, + }); + }; + initializeSound(); + }, []); + + return ( + + ); + }; +} - return ( - - ); - } - : null; +export { Video }; diff --git a/package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx b/package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx index 5711e84ce8..0fe17ca843 100644 --- a/package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx +++ b/package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx @@ -6,6 +6,7 @@ import Animated, { SharedValue } from 'react-native-reanimated'; import { isVideoPlayerAvailable, NativeHandlers, + PlaybackStatus, VideoPayloadData, VideoProgressData, VideoType, @@ -97,6 +98,37 @@ export const AnimatedGalleryVideo = React.memo( } }; + const onPlayBackStatusUpdate = (playbackStatus: PlaybackStatus) => { + if (!playbackStatus.isLoaded) { + // Update your UI for the unloaded state + setOpacity(1); + if (playbackStatus.error) { + console.error(`Encountered a fatal error during playback: ${playbackStatus.error}`); + } + } else { + // Update your UI for the loaded state + setOpacity(0); + handleLoad(attachmentId, playbackStatus.durationMillis); + if (playbackStatus.isPlaying) { + // Update your UI for the playing state + handleProgress( + attachmentId, + playbackStatus.positionMillis / playbackStatus.durationMillis, + ); + } + + if (playbackStatus.isBuffering) { + // Update your UI for the buffering state + setOpacity(1); + } + + if (playbackStatus.didJustFinish && !playbackStatus.isLooping) { + // The player has just finished playing and will stop. Maybe you want to play something else? + handleEnd(); + } + } + }; + const animatedStyles = useAnimatedGalleryStyle({ index, offsetScale, @@ -130,6 +162,7 @@ export const AnimatedGalleryVideo = React.memo( onEnd={onEnd} onLoad={onLoad} onLoadStart={onLoadStart} + onPlaybackStatusUpdate={onPlayBackStatusUpdate} onProgress={onProgress} paused={paused} repeat={repeat} diff --git a/package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx b/package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx index 95c55fd937..18ff62f08f 100644 --- a/package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx +++ b/package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx @@ -63,7 +63,11 @@ export const ImageGalleryVideoControl = React.memo( // Note: Not particularly sure why this was ever added, but // will keep it for now for backwards compatibility. if (progress === 1) { - // For expo CLI + // For expo CLI, expo-av + if (videoRef.current?.setPositionAsync) { + await videoRef.current.setPositionAsync(0); + } + // For expo CLI, expo-video if (videoRef.current?.replay) { await videoRef.current.replay(); }