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:
parent
051e884c8f
commit
d6941392e0
@ -7,6 +7,9 @@
|
|||||||
"plugin:@typescript-eslint/eslint-recommended",
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
"plugin:@typescript-eslint/recommended"
|
"plugin:@typescript-eslint/recommended"
|
||||||
],
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-trailing-spaces": 1
|
||||||
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"requireConfigFile": false
|
"requireConfigFile": false
|
||||||
}
|
}
|
||||||
|
@ -29,17 +29,6 @@ Indicates whether the player allows switching to external playback mode such as
|
|||||||
- **true (default)** - allow switching to external playback mode
|
- **true (default)** - allow switching to external playback mode
|
||||||
- **false** - Don't 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`
|
### `audioOutput`
|
||||||
|
|
||||||
<PlatformsList types={['Android', 'iOS', 'visionOS']} />
|
<PlatformsList types={['Android', 'iOS', 'visionOS']} />
|
||||||
|
@ -36,6 +36,7 @@ import Video, {
|
|||||||
OnTextTrackDataChangedData,
|
OnTextTrackDataChangedData,
|
||||||
TextTrackType,
|
TextTrackType,
|
||||||
ISO639_1,
|
ISO639_1,
|
||||||
|
OnSeekData,
|
||||||
OnPlaybackStateChangedData,
|
OnPlaybackStateChangedData,
|
||||||
OnPlaybackRateChangeData,
|
OnPlaybackRateChangeData,
|
||||||
} from 'react-native-video';
|
} from 'react-native-video';
|
||||||
@ -68,6 +69,7 @@ interface StateType {
|
|||||||
srcListId: number;
|
srcListId: number;
|
||||||
loop: boolean;
|
loop: boolean;
|
||||||
showRNVControls: boolean;
|
showRNVControls: boolean;
|
||||||
|
poster?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoPlayer extends Component {
|
class VideoPlayer extends Component {
|
||||||
@ -95,6 +97,7 @@ class VideoPlayer extends Component {
|
|||||||
srcListId: 0,
|
srcListId: 0,
|
||||||
loop: false,
|
loop: false,
|
||||||
showRNVControls: false,
|
showRNVControls: false,
|
||||||
|
poster: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
seekerWidth = 0;
|
seekerWidth = 0;
|
||||||
@ -140,7 +143,7 @@ class VideoPlayer extends Component {
|
|||||||
type: TextTrackType.VTT,
|
type: TextTrackType.VTT,
|
||||||
uri: 'https://bitdash-a.akamaihd.net/content/sintel/subtitles/subtitles_en.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(
|
srcList = this.srcAllPlatformList.concat(
|
||||||
Platform.OS === 'android' ? this.srcAndroidList : this.srcIosList,
|
Platform.OS === 'android' ? this.srcAndroidList : this.srcIosList,
|
||||||
);
|
);
|
||||||
@ -223,14 +230,26 @@ class VideoPlayer extends Component {
|
|||||||
this.onTextTracks(data);
|
this.onTextTracks(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
onProgress = (data: OnProgressData) => {
|
updateSeeker = () => {
|
||||||
if (!this.state.seeking) {
|
// put this code in timeout as because it may be put just after a setState
|
||||||
|
setTimeout(()=> {
|
||||||
const position = this.calculateSeekerPosition();
|
const position = this.calculateSeekerPosition();
|
||||||
this.setSeekerPosition(position);
|
this.setSeekerPosition(position);
|
||||||
}
|
}, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
onProgress = (data: OnProgressData) => {
|
||||||
this.setState({currentTime: data.currentTime});
|
this.setState({currentTime: data.currentTime});
|
||||||
|
if (!this.state.seeking) {
|
||||||
|
this.updateSeeker()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onSeek = (data: OnSeekData) => {
|
||||||
|
this.setState({currentTime: data.currentTime});
|
||||||
|
this.updateSeeker()
|
||||||
|
}
|
||||||
|
|
||||||
onVideoLoadStart = () => {
|
onVideoLoadStart = () => {
|
||||||
console.log('onVideoLoadStart');
|
console.log('onVideoLoadStart');
|
||||||
this.setState({isLoading: true});
|
this.setState({isLoading: true});
|
||||||
@ -662,6 +681,16 @@ class VideoPlayer extends Component {
|
|||||||
}}
|
}}
|
||||||
text="decoration"
|
text="decoration"
|
||||||
/>
|
/>
|
||||||
|
<ToggleControl
|
||||||
|
isSelected={!!this.state.poster}
|
||||||
|
onPress={() => {
|
||||||
|
this.setState({
|
||||||
|
poster: this.state.poster ? undefined : this.samplePoster,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
selectedText="poster"
|
||||||
|
unselectedText="no poster"
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.generalControls}>
|
<View style={styles.generalControls}>
|
||||||
{/* shall be replaced by slider */}
|
{/* shall be replaced by slider */}
|
||||||
@ -810,11 +839,13 @@ class VideoPlayer extends Component {
|
|||||||
onAspectRatio={this.onAspectRatio}
|
onAspectRatio={this.onAspectRatio}
|
||||||
onReadyForDisplay={this.onReadyForDisplay}
|
onReadyForDisplay={this.onReadyForDisplay}
|
||||||
onBuffer={this.onVideoBuffer}
|
onBuffer={this.onVideoBuffer}
|
||||||
|
onSeek={this.onSeek}
|
||||||
repeat={this.state.loop}
|
repeat={this.state.loop}
|
||||||
selectedTextTrack={this.state.selectedTextTrack}
|
selectedTextTrack={this.state.selectedTextTrack}
|
||||||
selectedAudioTrack={this.state.selectedAudioTrack}
|
selectedAudioTrack={this.state.selectedAudioTrack}
|
||||||
playInBackground={false}
|
playInBackground={false}
|
||||||
preventsDisplaySleepDuringVideoPlayback={true}
|
preventsDisplaySleepDuringVideoPlayback={true}
|
||||||
|
poster={this.state.poster}
|
||||||
onPlaybackRateChange={this.onPlaybackRateChange}
|
onPlaybackRateChange={this.onPlaybackRateChange}
|
||||||
onPlaybackStateChanged={this.onPlaybackStateChanged}
|
onPlaybackStateChanged={this.onPlaybackStateChanged}
|
||||||
/>
|
/>
|
||||||
|
@ -681,21 +681,18 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
_pendingSeekTime = seekTime.floatValue
|
_pendingSeekTime = seekTime.floatValue
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let wasPaused = _paused
|
|
||||||
|
|
||||||
RCTPlayerOperations.seek(
|
RCTPlayerOperations.seek(
|
||||||
player: player,
|
player: player,
|
||||||
playerItem: item,
|
playerItem: item,
|
||||||
paused: wasPaused,
|
paused: _paused,
|
||||||
seekTime: seekTime.floatValue,
|
seekTime: seekTime.floatValue,
|
||||||
seekTolerance: seekTolerance.floatValue
|
seekTolerance: seekTolerance.floatValue
|
||||||
) { [weak self] (_: Bool) in
|
) { [weak self] (_: Bool) in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
self._playerObserver.addTimeObserverIfNotSet()
|
self._playerObserver.addTimeObserverIfNotSet()
|
||||||
if !wasPaused {
|
self.setPaused(_paused)
|
||||||
self.setPaused(false)
|
|
||||||
}
|
|
||||||
self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))),
|
self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))),
|
||||||
"seekTime": seekTime,
|
"seekTime": seekTime,
|
||||||
"target": self.reactTag])
|
"target": self.reactTag])
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
"test": "echo no test available",
|
"test": "echo no test available",
|
||||||
"check-ios": "scripts/swift-format.sh && scripts/swift-lint.sh && scripts/clang-format.sh",
|
"check-ios": "scripts/swift-format.sh && scripts/swift-lint.sh && scripts/clang-format.sh",
|
||||||
"check-android": "scripts/kotlin-lint.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"
|
"codegen": "node ./node_modules/react-native/scripts/generate-codegen-artifacts.js --path ./ ./output"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
@ -121,6 +121,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
setRestoreUserInterfaceForPIPStopCompletionHandler,
|
setRestoreUserInterfaceForPIPStopCompletionHandler,
|
||||||
] = useState<boolean | undefined>();
|
] = useState<boolean | undefined>();
|
||||||
|
|
||||||
|
const hasPoster = !!poster;
|
||||||
|
|
||||||
const posterStyle = useMemo<StyleProp<ImageStyle>>(
|
const posterStyle = useMemo<StyleProp<ImageStyle>>(
|
||||||
() => ({
|
() => ({
|
||||||
...StyleSheet.absoluteFillObject,
|
...StyleSheet.absoluteFillObject,
|
||||||
@ -286,19 +288,20 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
|
|
||||||
const onVideoLoadStart = useCallback(
|
const onVideoLoadStart = useCallback(
|
||||||
(e: NativeSyntheticEvent<OnLoadStartData>) => {
|
(e: NativeSyntheticEvent<OnLoadStartData>) => {
|
||||||
|
hasPoster && setShowPoster(true);
|
||||||
onLoadStart?.(e.nativeEvent);
|
onLoadStart?.(e.nativeEvent);
|
||||||
},
|
},
|
||||||
[onLoadStart],
|
[hasPoster, onLoadStart],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onVideoLoad = useCallback(
|
const onVideoLoad = useCallback(
|
||||||
(e: NativeSyntheticEvent<OnLoadData>) => {
|
(e: NativeSyntheticEvent<OnLoadData>) => {
|
||||||
if (Platform.OS === 'windows') {
|
if (Platform.OS === 'windows') {
|
||||||
setShowPoster(false);
|
hasPoster && setShowPoster(false);
|
||||||
}
|
}
|
||||||
onLoad?.(e.nativeEvent);
|
onLoad?.(e.nativeEvent);
|
||||||
},
|
},
|
||||||
[onLoad, setShowPoster],
|
[onLoad, hasPoster, setShowPoster],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onVideoError = useCallback(
|
const onVideoError = useCallback(
|
||||||
@ -388,9 +391,9 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const _onReadyForDisplay = useCallback(() => {
|
const _onReadyForDisplay = useCallback(() => {
|
||||||
setShowPoster(false);
|
hasPoster && setShowPoster(false);
|
||||||
onReadyForDisplay?.();
|
onReadyForDisplay?.();
|
||||||
}, [setShowPoster, onReadyForDisplay]);
|
}, [setShowPoster, hasPoster, onReadyForDisplay]);
|
||||||
|
|
||||||
const _onPictureInPictureStatusChanged = useCallback(
|
const _onPictureInPictureStatusChanged = useCallback(
|
||||||
(e: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => {
|
(e: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => {
|
||||||
@ -567,7 +570,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
_onReceiveAdEvent as (e: NativeSyntheticEvent<object>) => void
|
_onReceiveAdEvent as (e: NativeSyntheticEvent<object>) => void
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{showPoster ? (
|
{hasPoster && showPoster ? (
|
||||||
<Image style={posterStyle} source={{uri: poster}} />
|
<Image style={posterStyle} source={{uri: poster}} />
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
@ -186,7 +186,6 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
|
|||||||
drm?: Drm;
|
drm?: Drm;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
adTagUrl?: string;
|
adTagUrl?: string;
|
||||||
audioOnly?: boolean;
|
|
||||||
audioOutput?: AudioOutput; // Mobile
|
audioOutput?: AudioOutput; // Mobile
|
||||||
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
|
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
|
||||||
bufferConfig?: BufferConfig; // Android
|
bufferConfig?: BufferConfig; // Android
|
||||||
|
Loading…
Reference in New Issue
Block a user