Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions package/expo-package/src/optionalDependencies/AudioVideo.ts
Original file line number Diff line number Diff line change
@@ -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 };
136 changes: 89 additions & 47 deletions package/expo-package/src/optionalDependencies/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 (
<VideoComponent
allowsFullscreen
contentFit={resizeMode}
nativeControls={false}
player={player}
style={[style]}
/>
);
};
}
// 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 (
<VideoComponent
onPlaybackStatusUpdate={onPlaybackStatusUpdate}
ref={videoRef}
resizeMode={resizeMode}
shouldPlay={!paused}
source={{
uri,
}}
style={[style]}
/>
);
};
}

return (
<VideoComponent
allowsFullscreen
contentFit={resizeMode}
nativeControls={false}
player={player}
style={[style]}
/>
);
}
: null;
export { Video };
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Animated, { SharedValue } from 'react-native-reanimated';
import {
isVideoPlayerAvailable,
NativeHandlers,
PlaybackStatus,
VideoPayloadData,
VideoProgressData,
VideoType,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -130,6 +162,7 @@ export const AnimatedGalleryVideo = React.memo(
onEnd={onEnd}
onLoad={onLoad}
onLoadStart={onLoadStart}
onPlaybackStatusUpdate={onPlayBackStatusUpdate}
onProgress={onProgress}
paused={paused}
repeat={repeat}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down