fix: ensure poster works as expected and add it to the sample (#3643)

* fix: ensure poster works as expected and add it to the sample
* chore: drop audioOnly property as not implemented on any platform
* fix(ios): do not save pause state before seeking
* fix(ts): onPlaybackRateChangeData was not correctly typed
This commit is contained in:
Olivier Bouillet 2024-04-05 10:35:57 +02:00 committed by GitHub
parent 051e884c8f
commit d6941392e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 50 additions and 27 deletions

View File

@ -7,6 +7,9 @@
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"no-trailing-spaces": 1
},
"parserOptions": {
"requireConfigFile": false
}

View File

@ -29,17 +29,6 @@ Indicates whether the player allows switching to external playback mode such as
- **true (default)** - allow switching to external playback mode
- **false** - Don't allow switching to external playback mode
### `audioOnly`
<PlatformsList types={['All']} />
Indicates whether the player should only play the audio track and instead of displaying the video track, show the poster instead.
- **false (default)** - Display the video as normal
- **true** - Show the poster and play the audio
For this to work, the poster prop must be set.
### `audioOutput`
<PlatformsList types={['Android', 'iOS', 'visionOS']} />

View File

@ -36,6 +36,7 @@ import Video, {
OnTextTrackDataChangedData,
TextTrackType,
ISO639_1,
OnSeekData,
OnPlaybackStateChangedData,
OnPlaybackRateChangeData,
} from 'react-native-video';
@ -68,6 +69,7 @@ interface StateType {
srcListId: number;
loop: boolean;
showRNVControls: boolean;
poster?: string;
}
class VideoPlayer extends Component {
@ -95,6 +97,7 @@ class VideoPlayer extends Component {
srcListId: 0,
loop: false,
showRNVControls: false,
poster: undefined,
};
seekerWidth = 0;
@ -140,7 +143,7 @@ class VideoPlayer extends Component {
type: TextTrackType.VTT,
uri: 'https://bitdash-a.akamaihd.net/content/sintel/subtitles/subtitles_en.vtt',
},
]
],
},
];
@ -190,6 +193,10 @@ class VideoPlayer extends Component {
},
];
// poster which can be displayed
samplePoster =
'https://upload.wikimedia.org/wikipedia/commons/1/18/React_Native_Logo.png';
srcList = this.srcAllPlatformList.concat(
Platform.OS === 'android' ? this.srcAndroidList : this.srcIosList,
);
@ -223,14 +230,26 @@ class VideoPlayer extends Component {
this.onTextTracks(data);
};
onProgress = (data: OnProgressData) => {
if (!this.state.seeking) {
updateSeeker = () => {
// put this code in timeout as because it may be put just after a setState
setTimeout(()=> {
const position = this.calculateSeekerPosition();
this.setSeekerPosition(position);
}
}, 1)
}
onProgress = (data: OnProgressData) => {
this.setState({currentTime: data.currentTime});
if (!this.state.seeking) {
this.updateSeeker()
}
};
onSeek = (data: OnSeekData) => {
this.setState({currentTime: data.currentTime});
this.updateSeeker()
}
onVideoLoadStart = () => {
console.log('onVideoLoadStart');
this.setState({isLoading: true});
@ -662,6 +681,16 @@ class VideoPlayer extends Component {
}}
text="decoration"
/>
<ToggleControl
isSelected={!!this.state.poster}
onPress={() => {
this.setState({
poster: this.state.poster ? undefined : this.samplePoster,
});
}}
selectedText="poster"
unselectedText="no poster"
/>
</View>
<View style={styles.generalControls}>
{/* shall be replaced by slider */}
@ -810,11 +839,13 @@ class VideoPlayer extends Component {
onAspectRatio={this.onAspectRatio}
onReadyForDisplay={this.onReadyForDisplay}
onBuffer={this.onVideoBuffer}
onSeek={this.onSeek}
repeat={this.state.loop}
selectedTextTrack={this.state.selectedTextTrack}
selectedAudioTrack={this.state.selectedAudioTrack}
playInBackground={false}
preventsDisplaySleepDuringVideoPlayback={true}
poster={this.state.poster}
onPlaybackRateChange={this.onPlaybackRateChange}
onPlaybackStateChanged={this.onPlaybackStateChanged}
/>

View File

@ -681,21 +681,18 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
_pendingSeekTime = seekTime.floatValue
return
}
let wasPaused = _paused
RCTPlayerOperations.seek(
player: player,
playerItem: item,
paused: wasPaused,
paused: _paused,
seekTime: seekTime.floatValue,
seekTolerance: seekTolerance.floatValue
) { [weak self] (_: Bool) in
guard let self else { return }
self._playerObserver.addTimeObserverIfNotSet()
if !wasPaused {
self.setPaused(false)
}
self.setPaused(_paused)
self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))),
"seekTime": seekTime,
"target": self.reactTag])

View File

@ -48,6 +48,7 @@
"test": "echo no test available",
"check-ios": "scripts/swift-format.sh && scripts/swift-lint.sh && scripts/clang-format.sh",
"check-android": "scripts/kotlin-lint.sh",
"check-all": "yarn check-android; yarn check-ios; yarn lint",
"codegen": "node ./node_modules/react-native/scripts/generate-codegen-artifacts.js --path ./ ./output"
},
"files": [

View File

@ -121,6 +121,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
setRestoreUserInterfaceForPIPStopCompletionHandler,
] = useState<boolean | undefined>();
const hasPoster = !!poster;
const posterStyle = useMemo<StyleProp<ImageStyle>>(
() => ({
...StyleSheet.absoluteFillObject,
@ -286,19 +288,20 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
const onVideoLoadStart = useCallback(
(e: NativeSyntheticEvent<OnLoadStartData>) => {
hasPoster && setShowPoster(true);
onLoadStart?.(e.nativeEvent);
},
[onLoadStart],
[hasPoster, onLoadStart],
);
const onVideoLoad = useCallback(
(e: NativeSyntheticEvent<OnLoadData>) => {
if (Platform.OS === 'windows') {
setShowPoster(false);
hasPoster && setShowPoster(false);
}
onLoad?.(e.nativeEvent);
},
[onLoad, setShowPoster],
[onLoad, hasPoster, setShowPoster],
);
const onVideoError = useCallback(
@ -388,9 +391,9 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
);
const _onReadyForDisplay = useCallback(() => {
setShowPoster(false);
hasPoster && setShowPoster(false);
onReadyForDisplay?.();
}, [setShowPoster, onReadyForDisplay]);
}, [setShowPoster, hasPoster, onReadyForDisplay]);
const _onPictureInPictureStatusChanged = useCallback(
(e: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => {
@ -567,7 +570,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
_onReceiveAdEvent as (e: NativeSyntheticEvent<object>) => void
}
/>
{showPoster ? (
{hasPoster && showPoster ? (
<Image style={posterStyle} source={{uri: poster}} />
) : null}
</View>

View File

@ -186,7 +186,6 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
drm?: Drm;
style?: StyleProp<ViewStyle>;
adTagUrl?: string;
audioOnly?: boolean;
audioOutput?: AudioOutput; // Mobile
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
bufferConfig?: BufferConfig; // Android