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();
}