fix(android)!: rework video tracks management (#3778)
* fix: fix crash when invalid index type is provided and minor clean up * fix: review video track management. Fix index support and rework string vs int in tracks management * fix: ABR track selection check * fix: split track selector in sample and lint code * fix: ensure we don't report null fields * chore: improve tracks displayed * chore: start moving to selection by index only
This commit is contained in:
parent
dbd7d7aa2c
commit
cad5c4624c
@ -9,7 +9,7 @@ class VideoTrack {
|
|||||||
var height = 0
|
var height = 0
|
||||||
var bitrate = 0
|
var bitrate = 0
|
||||||
var codecs = ""
|
var codecs = ""
|
||||||
var id = -1
|
var index = -1
|
||||||
var trackId = ""
|
var trackId = ""
|
||||||
var isSelected = false
|
var isSelected = false
|
||||||
}
|
}
|
||||||
|
@ -194,9 +194,15 @@ public class VideoEventEmitter {
|
|||||||
WritableMap audioTrack = Arguments.createMap();
|
WritableMap audioTrack = Arguments.createMap();
|
||||||
audioTrack.putInt("index", i);
|
audioTrack.putInt("index", i);
|
||||||
audioTrack.putString("title", format.getTitle());
|
audioTrack.putString("title", format.getTitle());
|
||||||
audioTrack.putString("type", format.getMimeType());
|
if (format.getMimeType() != null) {
|
||||||
audioTrack.putString("language", format.getLanguage());
|
audioTrack.putString("type", format.getMimeType());
|
||||||
audioTrack.putInt("bitrate", format.getBitrate());
|
}
|
||||||
|
if (format.getLanguage() != null) {
|
||||||
|
audioTrack.putString("language", format.getLanguage());
|
||||||
|
}
|
||||||
|
if (format.getBitrate() > 0) {
|
||||||
|
audioTrack.putInt("bitrate", format.getBitrate());
|
||||||
|
}
|
||||||
audioTrack.putBoolean("selected", format.isSelected());
|
audioTrack.putBoolean("selected", format.isSelected());
|
||||||
waAudioTracks.pushMap(audioTrack);
|
waAudioTracks.pushMap(audioTrack);
|
||||||
}
|
}
|
||||||
@ -214,7 +220,8 @@ public class VideoEventEmitter {
|
|||||||
videoTrack.putInt("height",vTrack.getHeight());
|
videoTrack.putInt("height",vTrack.getHeight());
|
||||||
videoTrack.putInt("bitrate", vTrack.getBitrate());
|
videoTrack.putInt("bitrate", vTrack.getBitrate());
|
||||||
videoTrack.putString("codecs", vTrack.getCodecs());
|
videoTrack.putString("codecs", vTrack.getCodecs());
|
||||||
videoTrack.putInt("trackId",vTrack.getId());
|
videoTrack.putString("trackId", vTrack.getTrackId());
|
||||||
|
videoTrack.putInt("index", vTrack.getIndex());
|
||||||
videoTrack.putBoolean("selected", vTrack.isSelected());
|
videoTrack.putBoolean("selected", vTrack.isSelected());
|
||||||
waVideoTracks.pushMap(videoTrack);
|
waVideoTracks.pushMap(videoTrack);
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
private final AspectRatioFrameLayout layout;
|
private final AspectRatioFrameLayout layout;
|
||||||
private final ComponentListener componentListener;
|
private final ComponentListener componentListener;
|
||||||
private ExoPlayer player;
|
private ExoPlayer player;
|
||||||
private Context context;
|
private final Context context;
|
||||||
private ViewGroup.LayoutParams layoutParams;
|
private final ViewGroup.LayoutParams layoutParams;
|
||||||
private final FrameLayout adOverlayFrameLayout;
|
private final FrameLayout adOverlayFrameLayout;
|
||||||
|
|
||||||
private boolean useTextureView = true;
|
private boolean useTextureView = true;
|
||||||
|
@ -1428,13 +1428,14 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
videoTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
|
videoTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
|
||||||
if (format.codecs != null) videoTrack.setCodecs(format.codecs);
|
if (format.codecs != null) videoTrack.setCodecs(format.codecs);
|
||||||
videoTrack.setTrackId(format.id == null ? String.valueOf(trackIndex) : format.id);
|
videoTrack.setTrackId(format.id == null ? String.valueOf(trackIndex) : format.id);
|
||||||
|
videoTrack.setIndex(trackIndex);
|
||||||
return videoTrack;
|
return videoTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<VideoTrack> getVideoTrackInfo() {
|
private ArrayList<VideoTrack> getVideoTrackInfo() {
|
||||||
ArrayList<VideoTrack> videoTracks = new ArrayList<>();
|
ArrayList<VideoTrack> videoTracks = new ArrayList<>();
|
||||||
if (trackSelector == null) {
|
if (trackSelector == null) {
|
||||||
// Likely player is unmounting so no audio tracks are available anymore
|
// Likely player is unmounting so no video tracks are available anymore
|
||||||
return videoTracks;
|
return videoTracks;
|
||||||
}
|
}
|
||||||
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
||||||
@ -1869,14 +1870,15 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("index".equals(type)) {
|
} else if ("index".equals(type)) {
|
||||||
try {
|
int iValue = Integer.parseInt(value);
|
||||||
int iValue = Integer.parseInt(value);
|
|
||||||
if (iValue < groups.length) {
|
if (trackType == C.TRACK_TYPE_VIDEO && groups.length == 1) {
|
||||||
groupIndex = iValue;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
DebugLog.e(TAG, "cannot parse index:" + value);
|
|
||||||
groupIndex = 0;
|
groupIndex = 0;
|
||||||
|
if (iValue < groups.get(groupIndex).length) {
|
||||||
|
tracks.set(0, iValue);
|
||||||
|
}
|
||||||
|
} else if (iValue < groups.length) {
|
||||||
|
groupIndex = iValue;
|
||||||
}
|
}
|
||||||
} else if ("resolution".equals(type)) {
|
} else if ("resolution".equals(type)) {
|
||||||
int height = Integer.parseInt(value);
|
int height = Integer.parseInt(value);
|
||||||
|
@ -237,9 +237,9 @@ Example:
|
|||||||
{ title: '#3 English Director Commentary', language: 'en', index: 2, type: 'text/vtt' }
|
{ title: '#3 English Director Commentary', language: 'en', index: 2, type: 'text/vtt' }
|
||||||
],
|
],
|
||||||
videoTracks: [
|
videoTracks: [
|
||||||
{ bitrate: 3987904, codecs: "avc1.640028", height: 720, trackId: "f1-v1-x3", width: 1280 },
|
{ index: 0, bitrate: 3987904, codecs: "avc1.640028", height: 720, trackId: "f1-v1-x3", width: 1280 },
|
||||||
{ bitrate: 7981888, codecs: "avc1.640028", height: 1080, trackId: "f2-v1-x3", width: 1920 },
|
{ index: 1, bitrate: 7981888, codecs: "avc1.640028", height: 1080, trackId: "f2-v1-x3", width: 1920 },
|
||||||
{ bitrate: 1994979, codecs: "avc1.4d401f", height: 480, trackId: "f3-v1-x3", width: 848 }
|
{ index: 2, bitrate: 1994979, codecs: "avc1.4d401f", height: 480, trackId: "f3-v1-x3", width: 848 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -550,7 +550,8 @@ Payload:
|
|||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| -------- | ------- | ------------------------------------- |
|
| -------- | ------- | ------------------------------------- |
|
||||||
| trackId | number | Internal track ID |
|
| index | number | index of the track |
|
||||||
|
| trackId | string | Internal track ID |
|
||||||
| codecs | string | MimeType of codec used for this track |
|
| codecs | string | MimeType of codec used for this track |
|
||||||
| width | number | Track width |
|
| width | number | Track width |
|
||||||
| height | number | Track height |
|
| height | number | Track height |
|
||||||
@ -563,7 +564,8 @@ Example:
|
|||||||
{
|
{
|
||||||
videoTracks: [
|
videoTracks: [
|
||||||
{
|
{
|
||||||
trackId: 0,
|
index: O,
|
||||||
|
trackId: "0",
|
||||||
codecs: 'video/mp4',
|
codecs: 'video/mp4',
|
||||||
width: 1920,
|
width: 1920,
|
||||||
height: 1080,
|
height: 1080,
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
StyleSheet,
|
|
||||||
Text,
|
Text,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@ -15,8 +14,6 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import {Picker} from '@react-native-picker/picker';
|
|
||||||
|
|
||||||
import Video, {
|
import Video, {
|
||||||
AudioTrack,
|
AudioTrack,
|
||||||
OnAudioTracksData,
|
OnAudioTracksData,
|
||||||
@ -39,12 +36,33 @@ import Video, {
|
|||||||
OnSeekData,
|
OnSeekData,
|
||||||
OnPlaybackStateChangedData,
|
OnPlaybackStateChangedData,
|
||||||
OnPlaybackRateChangeData,
|
OnPlaybackRateChangeData,
|
||||||
|
OnVideoTracksData,
|
||||||
|
VideoTrack,
|
||||||
|
SelectedVideoTrackType,
|
||||||
|
SelectedVideoTrack,
|
||||||
BufferingStrategyType,
|
BufferingStrategyType,
|
||||||
|
ReactVideoSource,
|
||||||
|
Drm,
|
||||||
|
TextTracks,
|
||||||
} from 'react-native-video';
|
} from 'react-native-video';
|
||||||
import ToggleControl from './ToggleControl';
|
import ToggleControl from './ToggleControl';
|
||||||
import MultiValueControl, {
|
import MultiValueControl, {
|
||||||
MultiValueControlPropType,
|
MultiValueControlPropType,
|
||||||
} from './MultiValueControl';
|
} from './MultiValueControl';
|
||||||
|
import styles from './styles';
|
||||||
|
import AudioTrackSelector from './components/AudioTracksSelector';
|
||||||
|
import TextTrackSelector from './components/TextTracksSelector';
|
||||||
|
import VideoTrackSelector from './components/VideoTracksSelector';
|
||||||
|
|
||||||
|
type AdditionnalSourceInfo = {
|
||||||
|
textTracks: TextTracks;
|
||||||
|
adTagUrl: string;
|
||||||
|
description: string;
|
||||||
|
drm: Drm;
|
||||||
|
noView: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SampleVideoSource = ReactVideoSource | AdditionnalSourceInfo;
|
||||||
|
|
||||||
interface StateType {
|
interface StateType {
|
||||||
rate: number;
|
rate: number;
|
||||||
@ -65,8 +83,10 @@ interface StateType {
|
|||||||
seeking: boolean;
|
seeking: boolean;
|
||||||
audioTracks: Array<AudioTrack>;
|
audioTracks: Array<AudioTrack>;
|
||||||
textTracks: Array<TextTrack>;
|
textTracks: Array<TextTrack>;
|
||||||
|
videoTracks: Array<VideoTrack>;
|
||||||
selectedAudioTrack: SelectedTrack | undefined;
|
selectedAudioTrack: SelectedTrack | undefined;
|
||||||
selectedTextTrack: SelectedTrack | undefined;
|
selectedTextTrack: SelectedTrack | undefined;
|
||||||
|
selectedVideoTrack: SelectedVideoTrack;
|
||||||
srcListId: number;
|
srcListId: number;
|
||||||
loop: boolean;
|
loop: boolean;
|
||||||
showRNVControls: boolean;
|
showRNVControls: boolean;
|
||||||
@ -95,8 +115,12 @@ class VideoPlayer extends Component {
|
|||||||
seeking: false,
|
seeking: false,
|
||||||
audioTracks: [],
|
audioTracks: [],
|
||||||
textTracks: [],
|
textTracks: [],
|
||||||
|
videoTracks: [],
|
||||||
selectedAudioTrack: undefined,
|
selectedAudioTrack: undefined,
|
||||||
selectedTextTrack: undefined,
|
selectedTextTrack: undefined,
|
||||||
|
selectedVideoTrack: {
|
||||||
|
type: SelectedVideoTrackType.AUTO,
|
||||||
|
},
|
||||||
srcListId: 0,
|
srcListId: 0,
|
||||||
loop: false,
|
loop: false,
|
||||||
showRNVControls: false,
|
showRNVControls: false,
|
||||||
@ -108,7 +132,7 @@ class VideoPlayer extends Component {
|
|||||||
seekerWidth = 0;
|
seekerWidth = 0;
|
||||||
|
|
||||||
// internal usage change to index if you want to select tracks by index instead of lang
|
// internal usage change to index if you want to select tracks by index instead of lang
|
||||||
textTracksSelectionBy = 'lang';
|
textTracksSelectionBy = 'index';
|
||||||
|
|
||||||
srcAllPlatformList = [
|
srcAllPlatformList = [
|
||||||
{
|
{
|
||||||
@ -123,8 +147,9 @@ class VideoPlayer extends Component {
|
|||||||
subtitle: 'Test Subtitle',
|
subtitle: 'Test Subtitle',
|
||||||
artist: 'Test Artist',
|
artist: 'Test Artist',
|
||||||
description: 'Test Description',
|
description: 'Test Description',
|
||||||
imageUri: 'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png'
|
imageUri:
|
||||||
}
|
'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: '(hls|live) red bull tv',
|
description: '(hls|live) red bull tv',
|
||||||
@ -134,8 +159,9 @@ class VideoPlayer extends Component {
|
|||||||
subtitle: 'Custom Subtitle',
|
subtitle: 'Custom Subtitle',
|
||||||
artist: 'Custom Artist',
|
artist: 'Custom Artist',
|
||||||
description: 'Custom Description',
|
description: 'Custom Description',
|
||||||
imageUri: 'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png'
|
imageUri:
|
||||||
}
|
'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'invalid URL',
|
description: 'invalid URL',
|
||||||
@ -239,7 +265,7 @@ class VideoPlayer extends Component {
|
|||||||
samplePoster =
|
samplePoster =
|
||||||
'https://upload.wikimedia.org/wikipedia/commons/1/18/React_Native_Logo.png';
|
'https://upload.wikimedia.org/wikipedia/commons/1/18/React_Native_Logo.png';
|
||||||
|
|
||||||
srcList = this.srcAllPlatformList.concat(
|
srcList: SampleVideoSource[] = this.srcAllPlatformList.concat(
|
||||||
Platform.OS === 'android' ? this.srcAndroidList : this.srcIosList,
|
Platform.OS === 'android' ? this.srcAndroidList : this.srcIosList,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -270,6 +296,7 @@ class VideoPlayer extends Component {
|
|||||||
this.setState({duration: data.duration, loading: false});
|
this.setState({duration: data.duration, loading: false});
|
||||||
this.onAudioTracks(data);
|
this.onAudioTracks(data);
|
||||||
this.onTextTracks(data);
|
this.onTextTracks(data);
|
||||||
|
this.onVideoTracks(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateSeeker = () => {
|
updateSeeker = () => {
|
||||||
@ -301,12 +328,12 @@ class VideoPlayer extends Component {
|
|||||||
const selectedTrack = data.audioTracks?.find((x: AudioTrack) => {
|
const selectedTrack = data.audioTracks?.find((x: AudioTrack) => {
|
||||||
return x.selected;
|
return x.selected;
|
||||||
});
|
});
|
||||||
if (selectedTrack?.language) {
|
if (selectedTrack?.index) {
|
||||||
this.setState({
|
this.setState({
|
||||||
audioTracks: data.audioTracks,
|
audioTracks: data.audioTracks,
|
||||||
selectedAudioTrack: {
|
selectedAudioTrack: {
|
||||||
type: 'language',
|
type: SelectedVideoTrackType.INDEX,
|
||||||
value: selectedTrack?.language,
|
value: selectedTrack?.index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -316,6 +343,13 @@ class VideoPlayer extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onVideoTracks = (data: OnVideoTracksData) => {
|
||||||
|
console.log('onVideoTracks', data.videoTracks);
|
||||||
|
this.setState({
|
||||||
|
videoTracks: data.videoTracks,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onTextTracks = (data: OnTextTracksData) => {
|
onTextTracks = (data: OnTextTracksData) => {
|
||||||
const selectedTrack = data.textTracks?.find((x: TextTrack) => {
|
const selectedTrack = data.textTracks?.find((x: TextTrack) => {
|
||||||
return x?.selected;
|
return x?.selected;
|
||||||
@ -324,13 +358,16 @@ class VideoPlayer extends Component {
|
|||||||
if (selectedTrack?.language) {
|
if (selectedTrack?.language) {
|
||||||
this.setState({
|
this.setState({
|
||||||
textTracks: data.textTracks,
|
textTracks: data.textTracks,
|
||||||
selectedTextTrack: this.textTracksSelectionBy === 'index' ? {
|
selectedTextTrack:
|
||||||
type: 'index',
|
this.textTracksSelectionBy === 'index'
|
||||||
value: selectedTrack?.index,
|
? {
|
||||||
}: {
|
type: 'index',
|
||||||
type: 'language',
|
value: selectedTrack?.index,
|
||||||
value: selectedTrack?.language,
|
}
|
||||||
},
|
: {
|
||||||
|
type: 'language',
|
||||||
|
value: selectedTrack?.language,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -445,6 +482,9 @@ class VideoPlayer extends Component {
|
|||||||
textTracks: [],
|
textTracks: [],
|
||||||
selectedAudioTrack: undefined,
|
selectedAudioTrack: undefined,
|
||||||
selectedTextTrack: undefined,
|
selectedTextTrack: undefined,
|
||||||
|
selectedVideoTrack: {
|
||||||
|
type: SelectedVideoTrackType.AUTO,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,7 +681,8 @@ class VideoPlayer extends Component {
|
|||||||
return (
|
return (
|
||||||
<View style={styles.topControlsContainer}>
|
<View style={styles.topControlsContainer}>
|
||||||
<Text style={styles.controlOption}>
|
<Text style={styles.controlOption}>
|
||||||
{this.srcList[this.state.srcListId]?.description || 'local file'}
|
{(this.srcList[this.state.srcListId] as AdditionnalSourceInfo)
|
||||||
|
?.description || 'local file'}
|
||||||
</Text>
|
</Text>
|
||||||
<View>
|
<View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -667,6 +708,50 @@ class VideoPlayer extends Component {
|
|||||||
this.setState({resizeMode: value});
|
this.setState({resizeMode: value});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onSelectedAudioTrackChange = (itemValue: string) => {
|
||||||
|
console.log('on audio value change ' + itemValue);
|
||||||
|
if (itemValue === 'none') {
|
||||||
|
this.setState({
|
||||||
|
selectedAudioTrack: SelectedVideoTrackType.DISABLED,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
selectedAudioTrack: {
|
||||||
|
type: SelectedVideoTrackType.INDEX,
|
||||||
|
value: itemValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onSelectedTextTrackChange = (itemValue: string) => {
|
||||||
|
console.log('on value change ' + itemValue);
|
||||||
|
this.setState({
|
||||||
|
selectedTextTrack: {
|
||||||
|
type: this.textTracksSelectionBy === 'index' ? 'index' : 'language',
|
||||||
|
value: itemValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onSelectedVideoTrackChange = (itemValue: string) => {
|
||||||
|
console.log('on value change ' + itemValue);
|
||||||
|
if (itemValue === undefined || itemValue === 'auto') {
|
||||||
|
this.setState({
|
||||||
|
selectedVideoTrack: {
|
||||||
|
type: SelectedVideoTrackType.AUTO,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
selectedVideoTrack: {
|
||||||
|
type: SelectedVideoTrackType.INDEX,
|
||||||
|
value: itemValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
renderOverlay() {
|
renderOverlay() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -809,77 +894,22 @@ class VideoPlayer extends Component {
|
|||||||
</View>
|
</View>
|
||||||
{this.renderSeekBar()}
|
{this.renderSeekBar()}
|
||||||
<View style={styles.generalControls}>
|
<View style={styles.generalControls}>
|
||||||
<Text style={styles.controlOption}>AudioTrack</Text>
|
<AudioTrackSelector
|
||||||
{this.state.audioTracks?.length <= 0 ? (
|
audioTracks={this.state.audioTracks}
|
||||||
<Text style={styles.emptyPickerItem}>empty</Text>
|
selectedAudioTrack={this.state.selectedAudioTrack}
|
||||||
) : (
|
onValueChange={this.onSelectedAudioTrackChange}
|
||||||
<Picker
|
/>
|
||||||
style={styles.picker}
|
<TextTrackSelector
|
||||||
itemStyle={styles.pickerItem}
|
textTracks={this.state.textTracks}
|
||||||
selectedValue={this.state.selectedAudioTrack?.value}
|
selectedTextTrack={this.state.selectedTextTrack}
|
||||||
onValueChange={itemValue => {
|
onValueChange={this.onSelectedTextTrackChange}
|
||||||
console.log('on audio value change ' + itemValue);
|
textTracksSelectionBy={this.textTracksSelectionBy}
|
||||||
this.setState({
|
/>
|
||||||
selectedAudioTrack: {
|
<VideoTrackSelector
|
||||||
type: 'language',
|
videoTracks={this.state.videoTracks}
|
||||||
value: itemValue,
|
selectedVideoTrack={this.state.selectedVideoTrack}
|
||||||
},
|
onValueChange={this.onSelectedVideoTrackChange}
|
||||||
});
|
/>
|
||||||
}}>
|
|
||||||
{this.state.audioTracks.map(track => {
|
|
||||||
if (!track) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Picker.Item
|
|
||||||
label={track.language}
|
|
||||||
value={track.language}
|
|
||||||
key={track.language}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Picker>
|
|
||||||
)}
|
|
||||||
<Text style={styles.controlOption}>TextTrack</Text>
|
|
||||||
{this.state.textTracks?.length <= 0 ? (
|
|
||||||
<Text style={styles.emptyPickerItem}>empty</Text>
|
|
||||||
) : (
|
|
||||||
<Picker
|
|
||||||
style={styles.picker}
|
|
||||||
itemStyle={styles.pickerItem}
|
|
||||||
selectedValue={this.state.selectedTextTrack?.value}
|
|
||||||
onValueChange={itemValue => {
|
|
||||||
console.log('on value change ' + itemValue);
|
|
||||||
this.setState({
|
|
||||||
selectedTextTrack: {
|
|
||||||
type: this.textTracksSelectionBy === 'index' ? 'index': 'language',
|
|
||||||
value: itemValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}>
|
|
||||||
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
|
||||||
{this.state.textTracks.map(track => {
|
|
||||||
if (!track) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.textTracksSelectionBy === 'index') {
|
|
||||||
return (
|
|
||||||
<Picker.Item
|
|
||||||
label={`${track.index}`}
|
|
||||||
value={track.index}
|
|
||||||
key={track.index}
|
|
||||||
/>);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Picker.Item
|
|
||||||
label={track.language}
|
|
||||||
value={track.language}
|
|
||||||
key={track.language}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</Picker>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
@ -893,6 +923,9 @@ class VideoPlayer extends Component {
|
|||||||
? styles.fullScreen
|
? styles.fullScreen
|
||||||
: styles.halfScreen;
|
: styles.halfScreen;
|
||||||
|
|
||||||
|
const currentSrc = this.srcList[this.state.srcListId];
|
||||||
|
const additionnal = currentSrc as AdditionnalSourceInfo;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={viewStyle}>
|
<TouchableOpacity style={viewStyle}>
|
||||||
<Video
|
<Video
|
||||||
@ -900,10 +933,10 @@ class VideoPlayer extends Component {
|
|||||||
ref={(ref: VideoRef) => {
|
ref={(ref: VideoRef) => {
|
||||||
this.video = ref;
|
this.video = ref;
|
||||||
}}
|
}}
|
||||||
source={this.srcList[this.state.srcListId]}
|
source={currentSrc as ReactVideoSource}
|
||||||
textTracks={this.srcList[this.state.srcListId]?.textTracks}
|
textTracks={additionnal?.textTracks}
|
||||||
adTagUrl={this.srcList[this.state.srcListId]?.adTagUrl}
|
adTagUrl={additionnal?.adTagUrl}
|
||||||
drm={this.srcList[this.state.srcListId]?.drm}
|
drm={additionnal?.drm}
|
||||||
style={viewStyle}
|
style={viewStyle}
|
||||||
rate={this.state.rate}
|
rate={this.state.rate}
|
||||||
paused={this.state.paused}
|
paused={this.state.paused}
|
||||||
@ -915,6 +948,7 @@ class VideoPlayer extends Component {
|
|||||||
onLoad={this.onLoad}
|
onLoad={this.onLoad}
|
||||||
onAudioTracks={this.onAudioTracks}
|
onAudioTracks={this.onAudioTracks}
|
||||||
onTextTracks={this.onTextTracks}
|
onTextTracks={this.onTextTracks}
|
||||||
|
onVideoTracks={this.onVideoTracks}
|
||||||
onTextTrackDataChanged={this.onTextTrackDataChanged}
|
onTextTrackDataChanged={this.onTextTrackDataChanged}
|
||||||
onProgress={this.onProgress}
|
onProgress={this.onProgress}
|
||||||
onEnd={this.onEnd}
|
onEnd={this.onEnd}
|
||||||
@ -930,6 +964,7 @@ class VideoPlayer extends Component {
|
|||||||
repeat={this.state.loop}
|
repeat={this.state.loop}
|
||||||
selectedTextTrack={this.state.selectedTextTrack}
|
selectedTextTrack={this.state.selectedTextTrack}
|
||||||
selectedAudioTrack={this.state.selectedAudioTrack}
|
selectedAudioTrack={this.state.selectedAudioTrack}
|
||||||
|
selectedVideoTrack={this.state.selectedVideoTrack}
|
||||||
playInBackground={false}
|
playInBackground={false}
|
||||||
bufferConfig={{
|
bufferConfig={{
|
||||||
minBufferMs: 15000,
|
minBufferMs: 15000,
|
||||||
@ -955,7 +990,7 @@ class VideoPlayer extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{this.srcList[this.state.srcListId]?.noView
|
{(this.srcList[this.state.srcListId] as AdditionnalSourceInfo)?.noView
|
||||||
? null
|
? null
|
||||||
: this.renderVideoView()}
|
: this.renderVideoView()}
|
||||||
{this.renderOverlay()}
|
{this.renderOverlay()}
|
||||||
@ -963,168 +998,4 @@ class VideoPlayer extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: 'black',
|
|
||||||
},
|
|
||||||
halfScreen: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 50,
|
|
||||||
left: 50,
|
|
||||||
bottom: 100,
|
|
||||||
right: 100,
|
|
||||||
},
|
|
||||||
fullScreen: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
},
|
|
||||||
bottomControls: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderRadius: 5,
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 20,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
leftControls: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderRadius: 5,
|
|
||||||
position: 'absolute',
|
|
||||||
top: 20,
|
|
||||||
bottom: 20,
|
|
||||||
left: 20,
|
|
||||||
},
|
|
||||||
rightControls: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderRadius: 5,
|
|
||||||
position: 'absolute',
|
|
||||||
top: 20,
|
|
||||||
bottom: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
topControls: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderRadius: 4,
|
|
||||||
position: 'absolute',
|
|
||||||
top: 20,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
overflow: 'hidden',
|
|
||||||
paddingBottom: 10,
|
|
||||||
},
|
|
||||||
generalControls: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
borderRadius: 4,
|
|
||||||
overflow: 'hidden',
|
|
||||||
paddingBottom: 10,
|
|
||||||
},
|
|
||||||
rateControl: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
volumeControl: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
resizeModeControl: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
leftRightControlOption: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
fontSize: 11,
|
|
||||||
color: 'white',
|
|
||||||
padding: 10,
|
|
||||||
lineHeight: 12,
|
|
||||||
},
|
|
||||||
controlOption: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
fontSize: 11,
|
|
||||||
color: 'white',
|
|
||||||
paddingLeft: 2,
|
|
||||||
paddingRight: 2,
|
|
||||||
lineHeight: 12,
|
|
||||||
},
|
|
||||||
pickerContainer: {
|
|
||||||
width: 100,
|
|
||||||
alignSelf: 'center',
|
|
||||||
color: 'white',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: 'red',
|
|
||||||
},
|
|
||||||
IndicatorStyle: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
seekbarContainer: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
borderRadius: 4,
|
|
||||||
height: 30,
|
|
||||||
},
|
|
||||||
seekbarTrack: {
|
|
||||||
backgroundColor: '#333',
|
|
||||||
height: 1,
|
|
||||||
position: 'relative',
|
|
||||||
top: 14,
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
seekbarFill: {
|
|
||||||
backgroundColor: '#FFF',
|
|
||||||
height: 1,
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
seekbarHandle: {
|
|
||||||
position: 'absolute',
|
|
||||||
marginLeft: -7,
|
|
||||||
height: 28,
|
|
||||||
width: 28,
|
|
||||||
},
|
|
||||||
seekbarCircle: {
|
|
||||||
borderRadius: 12,
|
|
||||||
position: 'relative',
|
|
||||||
top: 8,
|
|
||||||
left: 8,
|
|
||||||
height: 12,
|
|
||||||
width: 12,
|
|
||||||
},
|
|
||||||
picker: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
width: 100,
|
|
||||||
height: 40,
|
|
||||||
},
|
|
||||||
pickerItem: {
|
|
||||||
color: 'white',
|
|
||||||
width: 100,
|
|
||||||
height: 40,
|
|
||||||
},
|
|
||||||
emptyPickerItem: {
|
|
||||||
color: 'white',
|
|
||||||
marginTop: 20,
|
|
||||||
marginLeft: 20,
|
|
||||||
flex: 1,
|
|
||||||
width: 100,
|
|
||||||
height: 40,
|
|
||||||
},
|
|
||||||
topControlsContainer: {
|
|
||||||
paddingTop: 30,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default VideoPlayer;
|
export default VideoPlayer;
|
||||||
|
53
examples/basic/src/components/AudioTracksSelector.tsx
Normal file
53
examples/basic/src/components/AudioTracksSelector.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {Picker} from '@react-native-picker/picker';
|
||||||
|
import {Text} from 'react-native';
|
||||||
|
import {AudioTrack, SelectedTrack} from 'react-native-video';
|
||||||
|
import styles from '../styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export interface AudioTrackSelectorType {
|
||||||
|
audioTracks: Array<AudioTrack>;
|
||||||
|
selectedAudioTrack: SelectedTrack | undefined;
|
||||||
|
onValueChange: (arg0: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AudioTrackSelector = ({
|
||||||
|
audioTracks,
|
||||||
|
selectedAudioTrack,
|
||||||
|
onValueChange,
|
||||||
|
}: AudioTrackSelectorType) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={styles.controlOption}>AudioTrack</Text>
|
||||||
|
<Picker
|
||||||
|
style={styles.picker}
|
||||||
|
itemStyle={styles.pickerItem}
|
||||||
|
selectedValue={selectedAudioTrack?.value}
|
||||||
|
onValueChange={itemValue => {
|
||||||
|
if (itemValue !== 'empty') {
|
||||||
|
console.log('on audio value change ' + itemValue);
|
||||||
|
onValueChange(`${itemValue}`);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{audioTracks?.length <= 0 ? (
|
||||||
|
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
|
||||||
|
) : (
|
||||||
|
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
||||||
|
)}
|
||||||
|
{audioTracks.map(track => {
|
||||||
|
if (!track) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Picker.Item
|
||||||
|
label={`${track.language} - ${track.title} - ${track.selected}`}
|
||||||
|
value={`${track.index}`}
|
||||||
|
key={`${track.index}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Picker>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AudioTrackSelector;
|
64
examples/basic/src/components/TextTracksSelector.tsx
Normal file
64
examples/basic/src/components/TextTracksSelector.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import {Picker} from '@react-native-picker/picker';
|
||||||
|
import {Text} from 'react-native';
|
||||||
|
import {TextTrack, SelectedTrack} from 'react-native-video';
|
||||||
|
import styles from '../styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export interface TextTrackSelectorType {
|
||||||
|
textTracks: Array<TextTrack>;
|
||||||
|
selectedTextTrack: SelectedTrack | undefined;
|
||||||
|
onValueChange: (arg0: string) => void;
|
||||||
|
textTracksSelectionBy: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextTrackSelector = ({
|
||||||
|
textTracks,
|
||||||
|
selectedTextTrack,
|
||||||
|
onValueChange,
|
||||||
|
textTracksSelectionBy,
|
||||||
|
}: TextTrackSelectorType) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={styles.controlOption}>TextTrack</Text>
|
||||||
|
<Picker
|
||||||
|
style={styles.picker}
|
||||||
|
itemStyle={styles.pickerItem}
|
||||||
|
selectedValue={`${selectedTextTrack?.value}`}
|
||||||
|
onValueChange={itemValue => {
|
||||||
|
if (itemValue !== 'empty') {
|
||||||
|
onValueChange(itemValue);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{textTracks?.length <= 0 ? (
|
||||||
|
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
|
||||||
|
) : (
|
||||||
|
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
||||||
|
)}
|
||||||
|
{textTracks.map(track => {
|
||||||
|
if (!track) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (textTracksSelectionBy === 'index') {
|
||||||
|
return (
|
||||||
|
<Picker.Item
|
||||||
|
label={`${track.index}`}
|
||||||
|
value={track.index}
|
||||||
|
key={track.index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Picker.Item
|
||||||
|
label={track.language}
|
||||||
|
value={track.language}
|
||||||
|
key={track.language}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Picker>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextTrackSelector;
|
64
examples/basic/src/components/VideoTracksSelector.tsx
Normal file
64
examples/basic/src/components/VideoTracksSelector.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import {Picker} from '@react-native-picker/picker';
|
||||||
|
import {Text} from 'react-native';
|
||||||
|
import {
|
||||||
|
SelectedVideoTrack,
|
||||||
|
SelectedVideoTrackType,
|
||||||
|
VideoTrack,
|
||||||
|
} from 'react-native-video';
|
||||||
|
import styles from '../styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export interface VideoTrackSelectorType {
|
||||||
|
videoTracks: Array<VideoTrack>;
|
||||||
|
selectedVideoTrack: SelectedVideoTrack | undefined;
|
||||||
|
onValueChange: (arg0: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VideoTrackSelector = ({
|
||||||
|
videoTracks,
|
||||||
|
selectedVideoTrack,
|
||||||
|
onValueChange,
|
||||||
|
}: VideoTrackSelectorType) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={styles.controlOption}>VideoTrack</Text>
|
||||||
|
<Picker
|
||||||
|
style={styles.picker}
|
||||||
|
itemStyle={styles.pickerItem}
|
||||||
|
selectedValue={
|
||||||
|
selectedVideoTrack === undefined ||
|
||||||
|
selectedVideoTrack?.type === SelectedVideoTrackType.AUTO
|
||||||
|
? 'auto'
|
||||||
|
: `${selectedVideoTrack?.value}`
|
||||||
|
}
|
||||||
|
onValueChange={itemValue => {
|
||||||
|
if (itemValue !== 'empty') {
|
||||||
|
onValueChange(itemValue);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<Picker.Item label={'auto'} value={'auto'} key={'auto'} />
|
||||||
|
{videoTracks?.length <= 0 || videoTracks?.length <= 0 ? (
|
||||||
|
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
|
||||||
|
) : (
|
||||||
|
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
||||||
|
)}
|
||||||
|
{videoTracks?.map(track => {
|
||||||
|
if (!track) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Picker.Item
|
||||||
|
label={`${track.width}x${track.height} ${Math.floor(
|
||||||
|
(track.bitrate || 0) / 8 / 1024,
|
||||||
|
)} Kbps`}
|
||||||
|
value={`${track.index}`}
|
||||||
|
key={track.index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Picker>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VideoTrackSelector;
|
167
examples/basic/src/styles.tsx
Normal file
167
examples/basic/src/styles.tsx
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import {StyleSheet} from 'react-native';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
},
|
||||||
|
halfScreen: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 50,
|
||||||
|
left: 50,
|
||||||
|
bottom: 100,
|
||||||
|
right: 100,
|
||||||
|
},
|
||||||
|
fullScreen: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
bottomControls: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderRadius: 5,
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 20,
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
},
|
||||||
|
leftControls: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderRadius: 5,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 20,
|
||||||
|
bottom: 20,
|
||||||
|
left: 20,
|
||||||
|
},
|
||||||
|
rightControls: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderRadius: 5,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 20,
|
||||||
|
bottom: 20,
|
||||||
|
right: 20,
|
||||||
|
},
|
||||||
|
topControls: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderRadius: 4,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 20,
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
overflow: 'hidden',
|
||||||
|
paddingBottom: 10,
|
||||||
|
},
|
||||||
|
generalControls: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
borderRadius: 4,
|
||||||
|
overflow: 'hidden',
|
||||||
|
paddingBottom: 10,
|
||||||
|
},
|
||||||
|
rateControl: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
volumeControl: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
resizeModeControl: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
leftRightControlOption: {
|
||||||
|
alignSelf: 'center',
|
||||||
|
fontSize: 11,
|
||||||
|
color: 'white',
|
||||||
|
padding: 10,
|
||||||
|
lineHeight: 12,
|
||||||
|
},
|
||||||
|
controlOption: {
|
||||||
|
alignSelf: 'center',
|
||||||
|
fontSize: 11,
|
||||||
|
color: 'white',
|
||||||
|
paddingLeft: 2,
|
||||||
|
paddingRight: 2,
|
||||||
|
lineHeight: 12,
|
||||||
|
},
|
||||||
|
pickerContainer: {
|
||||||
|
width: 100,
|
||||||
|
alignSelf: 'center',
|
||||||
|
color: 'white',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'red',
|
||||||
|
},
|
||||||
|
IndicatorStyle: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
seekbarContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
borderRadius: 4,
|
||||||
|
height: 30,
|
||||||
|
},
|
||||||
|
seekbarTrack: {
|
||||||
|
backgroundColor: '#333',
|
||||||
|
height: 1,
|
||||||
|
position: 'relative',
|
||||||
|
top: 14,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
seekbarFill: {
|
||||||
|
backgroundColor: '#FFF',
|
||||||
|
height: 1,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
seekbarHandle: {
|
||||||
|
position: 'absolute',
|
||||||
|
marginLeft: -7,
|
||||||
|
height: 28,
|
||||||
|
width: 28,
|
||||||
|
},
|
||||||
|
seekbarCircle: {
|
||||||
|
borderRadius: 12,
|
||||||
|
position: 'relative',
|
||||||
|
top: 8,
|
||||||
|
left: 8,
|
||||||
|
height: 12,
|
||||||
|
width: 12,
|
||||||
|
},
|
||||||
|
picker: {
|
||||||
|
flex: 1,
|
||||||
|
color: 'white',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: 100,
|
||||||
|
height: 40,
|
||||||
|
},
|
||||||
|
pickerItem: {
|
||||||
|
color: 'white',
|
||||||
|
width: 100,
|
||||||
|
height: 40,
|
||||||
|
},
|
||||||
|
emptyPickerItem: {
|
||||||
|
color: 'white',
|
||||||
|
marginTop: 20,
|
||||||
|
marginLeft: 20,
|
||||||
|
flex: 1,
|
||||||
|
width: 100,
|
||||||
|
height: 40,
|
||||||
|
},
|
||||||
|
topControlsContainer: {
|
||||||
|
paddingTop: 30,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default styles;
|
@ -6,6 +6,6 @@
|
|||||||
"react": ["./node_modules/@types/react"]
|
"react": ["./node_modules/@types/react"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src", "**/*.js"],
|
"jsx": "react",
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
@ -222,13 +222,14 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
if (!selectedVideoTrack) {
|
if (!selectedVideoTrack) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const value = selectedVideoTrack?.value
|
const type = typeof selectedVideoTrack.value;
|
||||||
? `${selectedVideoTrack.value}`
|
if (type !== 'number' && type !== 'string') {
|
||||||
: undefined;
|
console.log('invalid type provided to selectedVideoTrack');
|
||||||
|
return;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type: selectedVideoTrack?.type,
|
type: selectedVideoTrack?.type,
|
||||||
value,
|
value: `${selectedVideoTrack.value}`,
|
||||||
};
|
};
|
||||||
}, [selectedVideoTrack]);
|
}, [selectedVideoTrack]);
|
||||||
|
|
||||||
|
@ -225,7 +225,8 @@ export type OnTextTrackDataChangedData = Readonly<{
|
|||||||
|
|
||||||
export type OnVideoTracksData = Readonly<{
|
export type OnVideoTracksData = Readonly<{
|
||||||
videoTracks: {
|
videoTracks: {
|
||||||
trackId: Int32;
|
index: Int32;
|
||||||
|
tracksId?: string;
|
||||||
codecs?: string;
|
codecs?: string;
|
||||||
width?: Float;
|
width?: Float;
|
||||||
height?: Float;
|
height?: Float;
|
||||||
|
@ -21,6 +21,7 @@ import type {
|
|||||||
|
|
||||||
export type AudioTrack = OnAudioTracksData['audioTracks'][number];
|
export type AudioTrack = OnAudioTracksData['audioTracks'][number];
|
||||||
export type TextTrack = OnTextTracksData['textTracks'][number];
|
export type TextTrack = OnTextTracksData['textTracks'][number];
|
||||||
|
export type VideoTrack = OnVideoTracksData['videoTracks'][number];
|
||||||
|
|
||||||
export type OnLoadData = Readonly<{
|
export type OnLoadData = Readonly<{
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
@ -48,6 +49,15 @@ export type OnLoadData = Readonly<{
|
|||||||
type?: WithDefault<'srt' | 'ttml' | 'vtt', 'srt'>;
|
type?: WithDefault<'srt' | 'ttml' | 'vtt', 'srt'>;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
}[];
|
}[];
|
||||||
|
videoTracks: {
|
||||||
|
index: number;
|
||||||
|
tracksID?: string;
|
||||||
|
codecs?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
bitrate?: number;
|
||||||
|
selected?: boolean;
|
||||||
|
}[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type OnTextTracksData = Readonly<{
|
export type OnTextTracksData = Readonly<{
|
||||||
|
@ -117,7 +117,7 @@ export enum SelectedVideoTrackType {
|
|||||||
|
|
||||||
export type SelectedVideoTrack = {
|
export type SelectedVideoTrack = {
|
||||||
type: SelectedVideoTrackType;
|
type: SelectedVideoTrackType;
|
||||||
value?: number;
|
value?: string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SubtitleStyle = {
|
export type SubtitleStyle = {
|
||||||
|
Loading…
Reference in New Issue
Block a user