UNPKG

5.72 kBJavaScriptView Raw
1// @flow
2
3import Asset from './Asset';
4
5// TODO add:
6// disableFocusOnAndroid
7// audio routes (at least did become noisy on android)
8// pan
9// pitch
10// API to explicitly request audio focus / session
11// API to select stream type on Android
12// subtitles API
13
14export type PlaybackSource = number | { uri: string } | Asset;
15
16export type PlaybackStatus =
17 | {
18 isLoaded: false,
19 androidImplementation?: string,
20 error?: string, // populated exactly once when an error forces the object to unload
21 }
22 | {
23 isLoaded: true,
24 androidImplementation?: string,
25
26 uri: string,
27
28 progressUpdateIntervalMillis: number,
29 durationMillis?: number,
30 positionMillis: number,
31 playableDurationMillis?: number,
32
33 shouldPlay: boolean,
34 isPlaying: boolean,
35 isBuffering: boolean,
36
37 rate: number,
38 shouldCorrectPitch: boolean,
39 volume: number,
40 isMuted: boolean,
41 isLooping: boolean,
42
43 didJustFinish: boolean, // true exactly once when the track plays to finish
44 };
45
46export type PlaybackStatusToSet = {
47 androidImplementation?: string,
48 progressUpdateIntervalMillis?: number,
49 positionMillis?: number,
50 shouldPlay?: boolean,
51 rate?: number,
52 shouldCorrectPitch?: boolean,
53 volume?: number,
54 isMuted?: boolean,
55 isLooping?: boolean,
56};
57
58export const _DEFAULT_PROGRESS_UPDATE_INTERVAL_MILLIS: number = 500;
59export const _DEFAULT_INITIAL_PLAYBACK_STATUS: PlaybackStatusToSet = {
60 positionMillis: 0,
61 progressUpdateIntervalMillis: _DEFAULT_PROGRESS_UPDATE_INTERVAL_MILLIS,
62 shouldPlay: false,
63 rate: 1.0,
64 shouldCorrectPitch: false,
65 volume: 1.0,
66 isMuted: false,
67 isLooping: false,
68};
69
70const _getAssetFromPlaybackSource = (source: ?PlaybackSource): ?Asset => {
71 if (source == null) {
72 return null;
73 }
74
75 let asset: ?Asset = null;
76 if (typeof source === 'number') {
77 asset = Asset.fromModule(source);
78 } else if ('constructor' in source && source.constructor.name === 'Asset') {
79 asset = source;
80 }
81 return asset;
82};
83
84export const _getURIFromSource = (source: ?PlaybackSource): ?string => {
85 let uri: ?string = null;
86 let asset: ?Asset = _getAssetFromPlaybackSource(source);
87 if (asset != null) {
88 uri = asset.localUri || asset.uri;
89 } else if (
90 source != null &&
91 typeof source !== 'number' &&
92 'uri' in source &&
93 typeof source.uri === 'string'
94 ) {
95 uri = source.uri;
96 }
97 return uri;
98};
99
100export const _throwErrorIfValuesOutOfBoundsInStatus = async (
101 status: PlaybackStatusToSet
102) => {
103 if (
104 typeof status.rate === 'number' &&
105 (status.rate < 0.0 || status.rate > 32.0)
106 ) {
107 throw new Error('Rate value must be between 0.0 and 32.0.');
108 }
109 if (
110 typeof status.volume === 'number' &&
111 (status.volume < 0.0 || status.volume > 1.0)
112 ) {
113 throw new Error('Volume value must be between 0.0 and 1.0.');
114 }
115};
116
117export const _getURIAndFullInitialStatusForLoadAsync = async (
118 source: ?PlaybackSource,
119 initialStatus: ?PlaybackStatusToSet,
120 downloadFirst: boolean
121): Promise<{ uri: string, fullInitialStatus: PlaybackStatusToSet }> => {
122 // Download first if necessary.
123 let asset: ?Asset = _getAssetFromPlaybackSource(source);
124 if (downloadFirst && asset != null) {
125 // TODO we can download remote uri too once @nikki93 has integrated this into Asset
126 await asset.downloadAsync();
127 }
128
129 // Get the URI
130 const uri: ?string = _getURIFromSource(source);
131 if (uri == null) {
132 throw new Error('Cannot load null source!');
133 }
134
135 // Get the full initial status
136 const fullInitialStatus: PlaybackStatusToSet =
137 initialStatus == null
138 ? _DEFAULT_INITIAL_PLAYBACK_STATUS
139 : {
140 ..._DEFAULT_INITIAL_PLAYBACK_STATUS,
141 ...initialStatus,
142 };
143 _throwErrorIfValuesOutOfBoundsInStatus(fullInitialStatus);
144
145 return { uri, fullInitialStatus };
146};
147
148export const _getUnloadedStatus = (error: ?string = null): PlaybackStatus => {
149 const status: Object = { isLoaded: false };
150 if (error) {
151 status.error = error;
152 }
153 return status;
154};
155
156// TODO Unify 8 native calls into 4 by folding out PlayerData and then setting it in the Video component.
157export const _COMMON_AV_PLAYBACK_METHODS = {
158 // The following are separately defined in each playback object:
159 // getStatusAsync
160 // setOnPlaybackStatusUpdate
161 // loadAsync
162 // unloadAsync
163 // setStatusAsync
164
165 async playAsync(): Promise<PlaybackStatus> {
166 return this.setStatusAsync({ shouldPlay: true });
167 },
168 async playFromPositionAsync(positionMillis: number): Promise<PlaybackStatus> {
169 return this.setStatusAsync({ positionMillis, shouldPlay: true });
170 },
171 async pauseAsync(): Promise<PlaybackStatus> {
172 return this.setStatusAsync({ shouldPlay: false });
173 },
174 async stopAsync(): Promise<PlaybackStatus> {
175 return this.setStatusAsync({ positionMillis: 0, shouldPlay: false });
176 },
177 async setPositionAsync(positionMillis: number): Promise<PlaybackStatus> {
178 return this.setStatusAsync({ positionMillis });
179 },
180 async setRateAsync(
181 rate: number,
182 shouldCorrectPitch: boolean
183 ): Promise<PlaybackStatus> {
184 return this.setStatusAsync({ rate, shouldCorrectPitch });
185 },
186 async setVolumeAsync(volume: number): Promise<PlaybackStatus> {
187 return this.setStatusAsync({ volume });
188 },
189 async setIsMutedAsync(isMuted: boolean): Promise<PlaybackStatus> {
190 return this.setStatusAsync({ isMuted });
191 },
192 async setIsLoopingAsync(isLooping: boolean): Promise<PlaybackStatus> {
193 return this.setStatusAsync({ isLooping });
194 },
195 async setProgressUpdateIntervalAsync(
196 progressUpdateIntervalMillis: number
197 ): Promise<PlaybackStatus> {
198 return this.setStatusAsync({ progressUpdateIntervalMillis });
199 },
200};