expo-video-subtitle 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/FORK.md +36 -5
- package/README.md +44 -1
- package/build/VideoPlayer.types.d.ts +19 -0
- package/build/VideoPlayer.types.d.ts.map +1 -1
- package/build/VideoPlayer.types.js.map +1 -1
- package/build/VideoView.types.d.ts +8 -1
- package/build/VideoView.types.d.ts.map +1 -1
- package/build/VideoView.types.js.map +1 -1
- package/ios/Records/SubtitleSource.swift +9 -0
- package/ios/VideoPlayerSubtitleSideload.swift +87 -9
- package/package.json +1 -1
- package/src/VideoPlayer.types.ts +20 -0
- package/src/VideoView.types.ts +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,67 @@ Changes inherited from the upstream base version are not repeated here — see t
|
|
|
7
7
|
[upstream changelog](https://github.com/expo/expo/blob/main/packages/expo-video/CHANGELOG.md)
|
|
8
8
|
for the history of everything that isn't subtitle related.
|
|
9
9
|
|
|
10
|
+
## 0.4.1
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **iOS:** `SubtitleSource.bottomOffset` now anchors the **bottom** of the subtitle block, the same way
|
|
15
|
+
Android does, so one value places captions identically on both platforms.
|
|
16
|
+
|
|
17
|
+
0.4.0 emitted a bare WebVTT `line:` percentage, which anchors the *top* of the cue box. A cue that
|
|
18
|
+
wrapped onto two lines therefore sat a full line lower on iOS than on Android — 5–13% of the video
|
|
19
|
+
height at typical caption sizes, comparable to the offset itself, and enough to drop the second line
|
|
20
|
+
back over the burned-in captions the offset exists to escape. The cue setting is now written as
|
|
21
|
+
`line:N%,end`, where the trailing `,end` is WebVTT's line alignment component and is what makes the
|
|
22
|
+
percentage refer to the bottom edge.
|
|
23
|
+
|
|
24
|
+
This also fixes `bottomOffset: 0`, which as a bare `line:100%` put the top of the cue box at the bottom
|
|
25
|
+
edge of the video and hid the subtitle completely. It now means "sit at the bottom", matching Android.
|
|
26
|
+
|
|
27
|
+
Worth knowing before upgrading: a WebVTT parser that predates the alignment component discards the
|
|
28
|
+
entire `line` setting rather than ignoring the component, so on such a parser captions would be left
|
|
29
|
+
unpositioned instead of top-anchored. Bottom anchoring cannot be expressed any other way — it is the
|
|
30
|
+
same setting — so confirm the offset still lands on your target OS versions.
|
|
31
|
+
|
|
32
|
+
- **iOS:** the cached rendition of a rewritten subtitle is keyed on the format version as well as on the
|
|
33
|
+
URL, language and offset. Without it, an install that had already rendered a given offset would be
|
|
34
|
+
handed the file written by the previous version and the fix above would appear not to work — on
|
|
35
|
+
exactly the devices being used to test it.
|
|
36
|
+
|
|
37
|
+
- **iOS:** the same cache key joins its parts on NUL instead of a hyphen. A hyphen occurs in both
|
|
38
|
+
variable parts — BCP-47 tags carry one (`pt-BR`) and so does almost every URL — so two different
|
|
39
|
+
subtitles could flatten to the same string and be served each other's rewritten file.
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
|
|
43
|
+
- **Android:** `subtitleStyle.bottomOffset` was documented as a fraction of the *view* height. It is a
|
|
44
|
+
fraction of the content frame, which under the default `contentFit="contain"` is the displayed picture
|
|
45
|
+
— `SubtitleView` sits inside `AspectRatioFrameLayout`, which `RESIZE_MODE_FIT` shrinks to the video's
|
|
46
|
+
aspect ratio. The distinction matters for a letterboxed source, and the old wording invited callers to
|
|
47
|
+
write letterbox compensation that would have been wrong.
|
|
48
|
+
|
|
49
|
+
## 0.4.0
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **iOS:** `SubtitleSource.bottomOffset` lifts a sideloaded subtitle track off the bottom of the video,
|
|
54
|
+
as a fraction of its height. It is for releases that carry burned-in subtitles, where a sideloaded
|
|
55
|
+
track would otherwise be drawn on top of them. Android has had the same thing since 0.1.0 as
|
|
56
|
+
`subtitleStyle.bottomOffset`.
|
|
57
|
+
|
|
58
|
+
It lives on the source, not on `subtitleStyle`, because that is the only place iOS can read it from
|
|
59
|
+
in time. AVFoundation renders legible tracks itself and `AVPlayerItem.textStyleRules` carries no
|
|
60
|
+
positioning, so the position has to travel inside the WebVTT — written in as a `line:` cue setting
|
|
61
|
+
while the subtitle is merged into the video. That merge runs from the `VideoSource` alone, before any
|
|
62
|
+
`VideoView` has handed the player a style, so a view prop could not drive it without rebuilding the
|
|
63
|
+
player item every time a video starts. The two consequences are that it covers sideloaded tracks only
|
|
64
|
+
and that changing it takes effect on the next source load.
|
|
65
|
+
|
|
66
|
+
Cues that already position themselves are left alone, matching how Android passes explicitly
|
|
67
|
+
positioned cues through. Note the anchor differs between platforms: a bare WebVTT `line:` percentage
|
|
68
|
+
fixes the *top* of the cue box, where Android fixes the bottom, so a cue that wraps onto two lines
|
|
69
|
+
grows downward on iOS.
|
|
70
|
+
|
|
10
71
|
## 0.3.0
|
|
11
72
|
|
|
12
73
|
### Added
|
package/FORK.md
CHANGED
|
@@ -6,7 +6,7 @@ and (b) pull a newer `expo-video` release into it **without losing the subtitle
|
|
|
6
6
|
- **Baseline:** `expo-video@56.1.4` (Expo SDK 56), as published on npm.
|
|
7
7
|
- **Native module name:** unchanged (`ExpoVideo`) — this is a drop-in replacement, not a coexisting package.
|
|
8
8
|
- **Design principle:** the fork is deliberately **surgical and additive**. 7 new source files hold
|
|
9
|
-
almost all of the logic (~1,
|
|
9
|
+
almost all of the logic (~1,270 lines); the 19 touched upstream files receive ~470 added lines and
|
|
10
10
|
only ~44 removed ones. Nothing upstream is rewritten — the removals are call-site signatures and a
|
|
11
11
|
handful of rewritten function bodies — so a version bump is a small, mechanical re-apply.
|
|
12
12
|
|
|
@@ -20,9 +20,9 @@ Measured against the published `expo-video@56.1.4` tarball. `+`/`-` are added/re
|
|
|
20
20
|
|
|
21
21
|
| File | Lines | Purpose |
|
|
22
22
|
| --- | --- | --- |
|
|
23
|
-
| `ios/Records/SubtitleSource.swift` |
|
|
23
|
+
| `ios/Records/SubtitleSource.swift` | 41 | Record for one sideloaded subtitle; `isSrt` detection; `bottomOffset`, which lives here rather than on `SubtitleStyle` because the composition is built from a `VideoSource` alone, before any `VideoView` has supplied a style. |
|
|
24
24
|
| `ios/Records/SubtitleStyle.swift` | 118 | Record for caption styling; hex→ARGB parsing; builds `[AVTextStyleRule]`. |
|
|
25
|
-
| `ios/VideoPlayerSubtitleSideload.swift` |
|
|
25
|
+
| `ios/VideoPlayerSubtitleSideload.swift` | 281 | Builds the `AVMutableComposition`, downloads remote subs, SRT→WebVTT converter, and `applyLinePosition`, which writes a WebVTT `line:N%,end` cue setting per cue for `bottomOffset` — the only way to position captions AVFoundation renders itself. **The trailing `,end` is the line alignment component, not a `line-align` setting** (there is no such setting; the six are `vertical`/`line`/`position`/`size`/`align`/`region`), and it is what anchors the percentage to the bottom edge, matching Android. A parser predating it drops the whole `line` setting, so there is no graceful degradation — verify on device. Cue timing lines are matched by **timestamp regex, not by searching for `-->`**: subtitle text may contain an arrow, and appending a cue setting to it prints the setting on screen. The temp-file hash includes `bottomOffset` *and* a `formatVersion`, joined on **NUL** — the offset so a changed value does not reuse the previous rendition, the version so an upgrade that changes the written form does not reuse the previous release's, and NUL because a hyphen occurs inside both BCP-47 tags and URLs and let two subtitles collide. |
|
|
26
26
|
| `android/…/records/SubtitleSource.kt` | 54 | Record → `MediaItem.SubtitleConfiguration` + MIME resolution. |
|
|
27
27
|
| `android/…/records/SubtitleStyle.kt` | 144 | Record → `CaptionStyleCompat`; hex colour parsing; font-family resolution (`res/font` → `ReactFontManager`); the `applyEmbeddedStyles` flag, which `SubtitleUtils` reads directly rather than `toCaptionStyle`. |
|
|
28
28
|
| `android/…/utils/StackingSubtitleParser.kt` | 300 | `SubtitleParser` for SubRip that segments the cue timeline and merges simultaneous cues into one stacked block, plus the `SubtitleParser.Factory` that routes SubRip to it. Assigns each cue a vertical slot it keeps for its whole time on screen — a finished cue's line is held blank (`\u200B`) so the cues above it do not drop. Merges through a `SpannableStringBuilder` so cue spans survive. |
|
|
@@ -36,7 +36,7 @@ Measured against the published `expo-video@56.1.4` tarball. `+`/`-` are added/re
|
|
|
36
36
|
|
|
37
37
|
| File | Δ | What to re-apply |
|
|
38
38
|
| --- | --- | --- |
|
|
39
|
-
| `src/VideoPlayer.types.ts` | +
|
|
39
|
+
| `src/VideoPlayer.types.ts` | +79 | `subtitleTracks?: SubtitleSource[]` on `VideoSourceObject`; the exported `SubtitleSource` type, including `bottomOffset`. |
|
|
40
40
|
| `src/VideoView.types.ts` | +129 | `subtitleStyle?: SubtitleStyle` on `VideoViewProps`; `SubtitleStyle` + `SubtitleEdgeType` types. |
|
|
41
41
|
| `src/index.ts` | +2 | Export `SubtitleStyle`, `SubtitleEdgeType` (`SubtitleSource` rides on `export type *`). |
|
|
42
42
|
| `src/VideoPlayer.web.tsx` | +5 / −1 | Hoist `JSON.stringify(source)` out of the `useMemo` dependency list into a `sourceKey` const. No behaviour change — it was already recomputed every render — but `react-hooks/use-memo` rejects non-simple expressions in a dependency array, and it was the single lint **error** in the package. |
|
|
@@ -182,7 +182,8 @@ npm pack --dry-run # expect build/, plugin/build/, LICENSE, native sources
|
|
|
182
182
|
# NO dev config (.prettierrc, eslint.config.js, *.tsbuildinfo) — see §1
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
The file count is a blunt but effective regression check: **
|
|
185
|
+
The file count is a blunt but effective regression check: **255 files** as of 0.4.1 (254 at 0.2.2, plus
|
|
186
|
+
`RoundedSubtitleBackground.kt` in 0.3.0). If a change moves
|
|
186
187
|
it, diff the list against the previous release and confirm every difference is intended —
|
|
187
188
|
`npm pack --dry-run --json` gives a machine-readable list. Both directions matter: an unexpected
|
|
188
189
|
*addition* means dev clutter leaked in, an unexpected *removal* means a consumer is missing something.
|
|
@@ -194,6 +195,36 @@ Neither is created by `expo-module configure`, so a sync that loses one breaks t
|
|
|
194
195
|
that reads like a tooling bug. A `prettier/prettier` warning after a sync is far more likely to mean
|
|
195
196
|
the config went missing than that the code is genuinely misformatted — check that first.
|
|
196
197
|
|
|
198
|
+
### iOS — running the subtitle converter without a host app
|
|
199
|
+
|
|
200
|
+
`SubtitleConverter` at the bottom of `ios/VideoPlayerSubtitleSideload.swift` imports only `Foundation`,
|
|
201
|
+
so it compiles and runs on its own. It is worth exercising on every change to it: `srtToVtt` and
|
|
202
|
+
`applyLinePosition` are pure string transforms whose failure mode is a silently malformed cue rather
|
|
203
|
+
than a crash — a cue setting appended to a *text* line prints on screen, and a percentage outside
|
|
204
|
+
`0`–`100` can cost the whole cue.
|
|
205
|
+
|
|
206
|
+
Extract the shipped source rather than retyping it, so the test cannot drift from what ships:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
awk '/^internal enum SubtitleConverter \{/,0' ios/VideoPlayerSubtitleSideload.swift \
|
|
210
|
+
> /tmp/swiftcheck/Converter.swift
|
|
211
|
+
sed -i '' '1i\
|
|
212
|
+
import Foundation
|
|
213
|
+
' /tmp/swiftcheck/Converter.swift
|
|
214
|
+
# Assertions go in main.swift — Swift allows top-level code only in a file with that name.
|
|
215
|
+
swiftc -o converttest Converter.swift main.swift && ./converttest
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Cases worth keeping covered: the emitted setting is `line:N%,end`; `0` and `1` map to `line:100%,end`
|
|
219
|
+
and `line:0%,end`; out-of-range offsets clamp; a text line containing `-->` is left alone; a cue already
|
|
220
|
+
carrying `line:` is passed through; an unrelated cue setting (`align:start`) is preserved while the cue
|
|
221
|
+
still moves; hourless timings are recognised; CRLF is normalised.
|
|
222
|
+
|
|
223
|
+
**A passing harness does not mean the cue renders where you asked.** It checks the bytes written, not
|
|
224
|
+
whether AVFoundation honours them. `line:` positioning is the part of this package with no automated
|
|
225
|
+
coverage at all — confirm it against a real player with a *two-line* cue, which is the case the
|
|
226
|
+
alignment component exists for.
|
|
227
|
+
|
|
197
228
|
### Android — compiling the parser without a host app
|
|
198
229
|
|
|
199
230
|
`utils/StackingSubtitleParser.kt` imports only `android.text.*` and `androidx.media3.*`, so it
|
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ const player = useVideoPlayer({
|
|
|
67
67
|
player.subtitleTrack = player.availableSubtitleTracks[0];
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
`SubtitleSource` fields: `uri` (local `file://` or remote `http(s)://`), `language` (BCP-47 / ISO 639), optional `label`, optional `format` (`'vtt' | 'srt'`, inferred from the extension when omitted), and optional `
|
|
70
|
+
`SubtitleSource` fields: `uri` (local `file://` or remote `http(s)://`), `language` (BCP-47 / ISO 639), optional `label`, optional `format` (`'vtt' | 'srt'`, inferred from the extension when omitted), optional `default`, and optional `bottomOffset` (iOS — see [Moving subtitles off burned-in captions](#moving-subtitles-off-burned-in-captions)).
|
|
71
71
|
|
|
72
72
|
| | Android | iOS |
|
|
73
73
|
| --- | --- | --- |
|
|
@@ -109,6 +109,49 @@ The empty line can also sit *between* two cues when the middle one finishes firs
|
|
|
109
109
|
left on screen, the stack resets and the next cue starts from the bottom again. This is Android-only;
|
|
110
110
|
on iOS the system renderer decides the layout.
|
|
111
111
|
|
|
112
|
+
### Moving subtitles off burned-in captions
|
|
113
|
+
|
|
114
|
+
Some releases have subtitles burned into the picture. A sideloaded track then lands in the same place
|
|
115
|
+
and the two overlap. Both platforms can lift the subtitles clear of them, but the setting lives in a
|
|
116
|
+
different place on each:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// Android — a VideoView prop, applies to every track, updates live
|
|
120
|
+
<VideoView player={player} subtitleStyle={{ bottomOffset: 0.22 }} />
|
|
121
|
+
|
|
122
|
+
// iOS — a field on the subtitle track itself
|
|
123
|
+
subtitleTracks: [{ uri: 'https://cdn.example.com/id.srt', language: 'id', bottomOffset: 0.22 }]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Both are a fraction of the video height (`0`–`1`) measured up from the bottom; larger values sit
|
|
127
|
+
higher. The Android default is `0.08`.
|
|
128
|
+
|
|
129
|
+
The split is not an oversight. On iOS the position has to travel inside the WebVTT itself — the system
|
|
130
|
+
renders legible tracks and `AVPlayerItem.textStyleRules` carries no positioning — so it is written in
|
|
131
|
+
while the subtitle is merged into the video. That happens from the `VideoSource` alone, before any
|
|
132
|
+
`VideoView` has supplied a style, so a style prop could not reach it without rebuilding the player item
|
|
133
|
+
on every load. Two consequences on iOS: it applies to **sideloaded tracks only**, and **changing it
|
|
134
|
+
takes effect the next time the source loads**.
|
|
135
|
+
|
|
136
|
+
| | Android | iOS |
|
|
137
|
+
| --- | --- | --- |
|
|
138
|
+
| Where | `subtitleStyle.bottomOffset` | `subtitleTracks[].bottomOffset` |
|
|
139
|
+
| Applies to | every track, embedded included | sideloaded tracks only |
|
|
140
|
+
| Updates | live | on next source load |
|
|
141
|
+
| Anchors | the bottom of the subtitle block | the bottom of the subtitle block |
|
|
142
|
+
| Cues that position themselves | left alone | left alone |
|
|
143
|
+
|
|
144
|
+
That last row is worth knowing: subtitles carrying their own placement — SubRip `{\anN}` tags, WebVTT
|
|
145
|
+
cues with a `line:` setting — were positioned deliberately by whoever wrote them, so neither platform
|
|
146
|
+
moves them.
|
|
147
|
+
|
|
148
|
+
The iOS cue setting is written as `line:N%,end`. The trailing `,end` is WebVTT's line alignment
|
|
149
|
+
component, and it is what makes the percentage refer to the bottom edge of the cue box — so the two
|
|
150
|
+
platforms anchor the same way and one value is correct on both. Note that a WebVTT parser predating
|
|
151
|
+
that component discards the whole `line` setting rather than ignoring the component, which would leave
|
|
152
|
+
captions unpositioned; there is no form that degrades more gently, since both behaviours come from the
|
|
153
|
+
same setting.
|
|
154
|
+
|
|
112
155
|
## Styling subtitles
|
|
113
156
|
|
|
114
157
|
Pass a `subtitleStyle` prop to `VideoView`. It applies to whichever subtitle track is currently displayed, whether embedded or sideloaded. Colors accept a hex string (`#RGB`, `#RRGGBB`, `#RRGGBBAA`) or `transparent`.
|
|
@@ -396,6 +396,25 @@ export type SubtitleSource = {
|
|
|
396
396
|
* @default false
|
|
397
397
|
*/
|
|
398
398
|
default?: boolean;
|
|
399
|
+
/**
|
|
400
|
+
* How far above the bottom of the video this track renders, as a fraction of the video height (0–1).
|
|
401
|
+
*
|
|
402
|
+
* Useful when a video already carries burned-in (hardcoded) subtitles and the sideloaded ones would
|
|
403
|
+
* otherwise be drawn on top of them.
|
|
404
|
+
*
|
|
405
|
+
* It sits here rather than on [`subtitleStyle`](#videoviewprops) because iOS can only apply it while
|
|
406
|
+
* the subtitle is being merged into the video, which happens from the `VideoSource` alone — before any
|
|
407
|
+
* `VideoView` has supplied a style. Changing it therefore takes effect the next time the source loads.
|
|
408
|
+
*
|
|
409
|
+
* > **Note:** cues that position themselves are left alone, so this does nothing for a WebVTT track
|
|
410
|
+
* > whose cues already carry a `line:` setting.
|
|
411
|
+
*
|
|
412
|
+
* On **Android** use [`subtitleStyle.bottomOffset`](#subtitlestylebottomoffset) instead, which does the
|
|
413
|
+
* same thing for every track at once and updates live.
|
|
414
|
+
*
|
|
415
|
+
* @platform ios
|
|
416
|
+
*/
|
|
417
|
+
bottomOffset?: number;
|
|
399
418
|
};
|
|
400
419
|
/**
|
|
401
420
|
* Contains information about any errors that the player encountered during the playback
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC","sourcesContent":["import { SharedObject } from 'expo';\n\nimport type { VideoPlayerEvents } from './VideoPlayerEvents.types';\nimport type { VideoThumbnail } from './VideoThumbnail';\n\n/**\n * A class that represents an instance of the video player.\n */\nexport declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {\n /**\n * Boolean value whether the player is currently playing.\n * > Use `play` and `pause` methods to control the playback.\n */\n readonly playing: boolean;\n\n /**\n * Determines whether the player should automatically replay after reaching the end of the video.\n * @default false\n */\n loop: boolean;\n\n /**\n * Determines whether the player should allow external playback.\n * @default true\n * @platform ios\n */\n allowsExternalPlayback: boolean;\n\n /**\n * Determines how the player will interact with other audio playing in the system.\n *\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n audioMixingMode: AudioMixingMode;\n\n /**\n * Boolean value whether the player is currently muted.\n * Setting this property to `true`/`false` will mute/unmute the player.\n * @default false\n */\n muted: boolean;\n\n /**\n * Float value indicating the current playback time in seconds.\n *\n * If the player is not yet playing, this value indicates the time position\n * at which playback will begin once the `play()` method is called.\n *\n * Setting `currentTime` to a new value seeks the player to the given time.\n * Check out the [`seekTolerance`](#seektolerance) property to configure the seeking precision.\n */\n currentTime: number;\n\n /**\n * The exact timestamp when the currently displayed video frame was sent from the server,\n * based on the `EXT-X-PROGRAM-DATE-TIME` tag in the livestream metadata.\n * If this metadata is missing, this property will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentLiveTimestamp: number | null;\n\n /**\n * Float value indicating the latency of the live stream in seconds.\n * If a livestream doesn't have the required metadata, this will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentOffsetFromLive: number | null;\n\n /**\n * Float value indicating the time offset from the live in seconds.\n * @platform ios\n */\n targetOffsetFromLive: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n */\n readonly duration: number;\n\n /**\n * Float value between `0` and `1.0` representing the current volume.\n * Muting the player doesn't affect the volume. In other words, when the player is muted, the volume is the same as\n * when unmuted. Similarly, setting the volume doesn't unmute the player.\n * @default 1.0\n */\n volume: number;\n\n /**\n * Boolean value indicating if the player should correct audio pitch when the playback speed changes.\n * @default true\n */\n preservesPitch: boolean;\n\n /**\n * Float value indicating the interval in seconds at which the player will emit the [`timeUpdate`](#videoplayerevents) event.\n * When the value is equal to `0`, the event will not be emitted.\n *\n * @default 0\n */\n timeUpdateEventInterval: number;\n\n /**\n * Float value between `0` and `16.0` indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean indicating if the player should keep the screen on while playing.\n *\n * > On Android, this property has an effect only when a [`VideoView`](#videoview) is visible. If you want to keep the screen awake at all times use [`expo-keep-awake`](./keep-awake/).\n *\n * @default true\n * @platform android\n * @platform ios\n */\n keepScreenOnWhilePlaying: boolean;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n */\n readonly isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n */\n readonly status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n *\n * > **Note**: On Android, `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)\n * > has to be `true` for the now playing notification to work.\n * @default false\n * @platform android\n * @platform ios\n */\n showNowPlayingNotification: boolean;\n\n /**\n * Determines whether the player should continue playing after the app enters the background.\n *\n * > **Note**: The `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)\n * > has to be `true` for the background playback to work.\n * @default false\n * @platform ios\n * @platform android\n */\n staysActiveInBackground: boolean;\n\n /**\n * Float value indicating how far the player has buffered the video in seconds.\n *\n * This value is 0 when the player has not buffered up to the current playback time.\n * When it's impossible to determine the buffer state (for example, when the player isn't playing any media), this value is -1.\n */\n readonly bufferedPosition: number;\n\n /**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * > You should provide a `BufferOptions` object when setting this property. Setting individual buffer properties is not supported.\n * @platform android\n * @platform ios\n */\n bufferOptions: BufferOptions;\n\n /**\n * Specifies the subtitle track which is currently displayed by the player. `null` when no subtitles are displayed.\n *\n * > To ensure a valid subtitle track, always assign one of the subtitle tracks from the [`availableSubtitleTracks`](#availablesubtitletracks) array.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n subtitleTrack: SubtitleTrack | null;\n\n /**\n * Specifies the audio track currently played by the player. `null` when no audio is played.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n audioTrack: AudioTrack | null;\n\n /**\n * An array of audio tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableAudioTracks: AudioTrack[];\n\n /**\n * An array of subtitle tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableSubtitleTracks: SubtitleTrack[];\n\n /**\n * Specifies the video track currently played by the player. `null` when no video is displayed.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n readonly videoTrack: VideoTrack | null;\n\n /**\n * An array of video tracks available for the current video.\n *\n * > On iOS, when using a HLS source, make sure that the uri contains `.m3u8` extension or that the [`contentType`](#contenttype) property of the [`VideoSource`](#videosource) has been set to `'hls'`. Otherwise, the video tracks will not be available.\n *\n * @platform android\n * @platform ios\n */\n readonly availableVideoTracks: VideoTrack[];\n\n /**\n * Indicates whether the player is currently playing back the media to an external device via AirPlay.\n *\n * @platform ios\n */\n readonly isExternalPlaybackActive: boolean;\n\n /**\n * Determines the time that the actual position seeked to may precede or exceed the requested seek position.\n *\n * This property affects the precision of setting the [`currentTime`](#currenttime) property and the [`seekBy`](#seekbyseconds) method, and on Android, it also affects the accuracy of the scrubber from the default native controls.\n *\n * By default, the player seeks to the exact requested time.\n *\n * > If you are trying to optimize for scrubbing (many frequent seeks), also see [`ScrubbingModeOptions`](#scrubbingmodeoptions-1).\n */\n seekTolerance: SeekTolerance;\n\n /**\n * Determines whether the scrubbing mode is enabled and what scrubbing optimizations should be enabled.\n *\n * > See [`SeekTolerance`](#seektolerance) to set the seeking tolerance, which can also affect the scrubbing performance.\n *\n */\n scrubbingModeOptions: ScrubbingModeOptions;\n\n /**\n * Initializes a new video player instance with the given source.\n *\n * @param source The source of the video to be played.\n * @param useSynchronousReplace Optional parameter, when `true` `source` from the first parameter will be loaded on the main thread.\n * @param playerBuilderOptions Options to apply to the player builder before the native constructor is invoked.\n * @hidden\n */\n constructor(\n source: VideoSource,\n useSynchronousReplace?: boolean,\n playerBuilderOptions?: PlayerBuilderOptions\n );\n\n /**\n * Resumes the player.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current source with a new one.\n *\n * > On iOS, this method loads the asset data synchronously on the UI thread and can block it for extended periods of time.\n * > Use `replaceAsync` to load the asset asynchronously and avoid UI lags.\n *\n * > This method will be deprecated in the future.\n */\n replace(source: VideoSource, disableWarning?: boolean): void;\n\n /**\n * Replaces the current source with a new one, while offloading loading of the asset to a different thread.\n *\n * > On Android and Web, this method is equivalent to `replace`.\n */\n replaceAsync(source: VideoSource): Promise<void>;\n\n /**\n * Seeks the playback by the given number of seconds. The time to which the player seeks may differ from the specified requested time for efficiency,\n * depending on the encoding and what is currently buffered by the player. Use this function to implement playback controls that seek by specific amount of time,\n * in which case, the actual time usually does not have to be precise. For frame accurate seeking, use the [`currentTime`](#currenttime) property.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n\n /**\n * Generates thumbnails from the currently played asset. The thumbnails are references to native images,\n * thus they can be used as a source of the `Image` component from `expo-image`.\n * @platform android\n * @platform ios\n */\n generateThumbnailsAsync(\n times: number | number[],\n options?: VideoThumbnailOptions\n ): Promise<VideoThumbnail[]>;\n}\n\n/**\n * Additional options for video thumbnails generation.\n */\nexport type VideoThumbnailOptions = {\n /**\n * If provided, the generated thumbnail will not exceed this width in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxWidth?: number;\n\n /**\n * If provided, the generated thumbnail will not exceed this height in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxHeight?: number;\n};\n\n/**\n * Describes the current status of the player.\n * - `idle`: The player is not playing or loading any videos.\n * - `loading`: The player is loading video data from the provided source\n * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.\n * - `error`: The player has encountered an error while loading or playing the video.\n */\nexport type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\nexport type VideoSource = string | number | null | VideoSourceObject;\n\nexport type VideoSourceObject = {\n /**\n * The URI of the video.\n *\n * On iOS, `PHAsset` URIs are supported, but can only be loaded using the [`replaceAsync`](#replaceasyncsource) method or the default [`VideoPlayer`](#videoplayer) constructor.\n *\n * This property is exclusive with the `assetId` property. When both are present, the `assetId` will be ignored.\n */\n uri?: string;\n\n /**\n * The asset ID of a local video asset, acquired with the `require` function.\n * This property is exclusive with the `uri` property. When both are present, the `assetId` will be ignored.\n */\n assetId?: number;\n\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\n\n /**\n * Specifies information which will be displayed in the now playing notification.\n * When undefined the player will display information contained in the video metadata.\n * @platform android\n * @platform ios\n */\n metadata?: VideoMetadata;\n\n /**\n * Specifies headers sent with the video request.\n * > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).\n * @platform android\n * @platform ios\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the player should use caching for the video.\n * > Due to platform limitations, the cache cannot be used with HLS video sources on iOS. Caching DRM-protected videos is not supported on Android and iOS.\n * @default false\n * @platform android\n * @platform ios\n */\n useCaching?: boolean;\n\n /**\n * Specifies the content type of the video source. When set to `'auto'`, the player will try to automatically determine the content type.\n *\n * You should use this property when playing HLS, SmoothStreaming or DASH videos from an uri, which does not contain a standardized extension for the corresponding media type.\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n contentType?: ContentType;\n\n /**\n * An array of external (sideloaded) subtitle files to attach to the video source.\n *\n * Use this to add subtitles to sources that don't declare their own subtitle tracks — most importantly\n * progressive `MP4` files, which (unlike `HLS`/`DASH` manifests) cannot advertise external subtitle renditions.\n * The sideloaded tracks are merged into the media and become available through\n * [`availableSubtitleTracks`](#availablesubtitletracks), so they can be selected with\n * [`subtitleTrack`](#subtitletrack) just like embedded tracks.\n *\n * > **Note (iOS):** sideloading is done by building an `AVMutableComposition`, which has platform limitations:\n * > the video must be a progressive (non-HLS) source without DRM, remote subtitle files are downloaded to a\n * > local cache first, and only `WebVTT` is natively supported (`SRT` files are converted to `WebVTT`\n * > automatically). Sideloading only runs on the asynchronous loading path (the default `VideoPlayer`\n * > constructor and [`replaceAsync`](#replaceasyncsource)), not on the synchronous `replace`.\n *\n * @platform android\n * @platform ios\n */\n subtitleTracks?: SubtitleSource[];\n};\n\n/**\n * Describes a single external (sideloaded) subtitle file that can be attached to a [`VideoSource`](#videosource).\n */\nexport type SubtitleSource = {\n /**\n * The URI of the subtitle file. Both local (`file://`) and remote (`http(s)://`) URIs are supported.\n */\n uri: string;\n\n /**\n * The BCP-47 / ISO 639 language code of the subtitle track, for example `en`, `pl`, `de`.\n * This value is used to identify and select the track and is shown in the native track selector.\n */\n language: string;\n\n /**\n * A human-readable label for the track, shown in the native subtitle selection UI.\n * When omitted, a label is derived from the `language`.\n */\n label?: string;\n\n /**\n * The format of the subtitle file.\n * When omitted, the format is inferred from the file extension (`.vtt` / `.srt`).\n *\n * - `vtt`: WebVTT subtitles (`text/vtt`).\n * - `srt`: SubRip subtitles (`application/x-subrip`). On iOS these are converted to WebVTT before sideloading.\n *\n * @default inferred from the uri extension\n */\n format?: 'vtt' | 'srt';\n\n /**\n * Whether this track should be marked as the default subtitle track.\n * @default false\n */\n default?: boolean;\n};\n\n/**\n * Contains information about any errors that the player encountered during the playback\n */\nexport type PlayerError = {\n message: string;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n * @platform android\n * @platform ios\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n * @platform android\n * @platform ios\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n * @platform android\n * @platform ios\n */\n artist?: string;\n /**\n * The uri of the video artwork.\n * @platform android\n * @platform ios\n */\n artwork?: string;\n};\n\n/**\n * Specifies which type of DRM to use:\n * - Android supports ClearKey, PlayReady and Widevine.\n * - iOS supports FairPlay.\n */\nexport type DRMType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';\n\n/**\n * Specifies DRM options which will be used by the player while loading the video.\n */\nexport type DRMOptions = {\n /**\n * Determines which type of DRM to use.\n */\n type: DRMType;\n\n /**\n * Determines the license server URL.\n */\n licenseServer: string;\n\n /**\n * Determines headers sent to the license server on license requests.\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the DRM is a multi-key DRM.\n * @platform android\n */\n multiKey?: boolean;\n\n /**\n * Specifies the content ID of the stream.\n * @platform ios\n */\n contentId?: string;\n\n /**\n * Specifies the certificate URL for the FairPlay DRM.\n * @platform ios\n */\n certificateUrl?: string;\n\n /**\n * Specifies the base64 encoded certificate data for the FairPlay DRM.\n * When this property is set, the `certificateUrl` property is ignored.\n * @platform ios\n */\n base64CertificateData?: string;\n};\n\n/**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * @platform android\n * @platform ios\n */\nexport type BufferOptions = {\n /**\n * The duration in seconds which determines how much media the player should buffer ahead of the current playback time.\n *\n * On iOS when set to `0` the player will automatically decide appropriate buffer duration.\n *\n * Equivalent to [`AVPlayerItem.preferredForwardBufferDuration`](https://developer.apple.com/documentation/avfoundation/avplayeritem/1643630-preferredforwardbufferduration).\n * @default Android: 20, iOS: 0\n * @platform android\n * @platform ios\n */\n readonly preferredForwardBufferDuration?: number;\n\n /**\n * A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling.\n *\n * Equivalent to [`AVPlayer.automaticallyWaitsToMinimizeStalling`](https://developer.apple.com/documentation/avfoundation/avplayer/1643482-automaticallywaitstominimizestal).\n * @default true\n * @platform ios\n */\n readonly waitsToMinimizeStalling?: boolean;\n\n /**\n * Minimum duration of the buffer in seconds required to continue playing after the player has been paused or started buffering.\n *\n * > This property will be ignored if `preferredForwardBufferDuration` is lower.\n * @default 2\n * @platform android\n */\n readonly minBufferForPlayback?: number;\n\n /**\n * The maximum number of bytes that the player can buffer from the network.\n * When 0 the player will automatically decide appropriate buffer size.\n *\n * @default 0\n * @platform android\n */\n readonly maxBufferBytes?: number | null;\n\n /**\n * A Boolean value which determines whether the player should prioritize time over size when buffering media.\n *\n * @default false\n * @platform android\n */\n readonly prioritizeTimeOverSizeThreshold?: boolean;\n};\n\n/**\n * Specifies the content type of the source.\n *\n * - `auto`: The player will automatically determine the content type of the video.\n * - `progressive`: The player will use progressive download content type. This is the default `ContentType` when the uri does not contain an extension.\n * - `hls`: The player will use HLS content type.\n * - `dash`: The player will use DASH content type (Android-only).\n * - `smoothStreaming`: The player will use SmoothStreaming content type (Android-only).\n *\n * @default `auto`\n */\nexport type ContentType = 'auto' | 'progressive' | 'hls' | 'dash' | 'smoothStreaming';\n\n/**\n * Specifies the audio mode that the player should use. Audio mode is set on per-app basis, if there are multiple players playing and\n * have different a `AudioMode` specified, the highest priority mode will be used. Priority order: 'doNotMix' > 'auto' > 'duckOthers' > 'mixWithOthers'.\n *\n * - `mixWithOthers`: The player will mix its audio output with other apps.\n * - `duckOthers`: The player will lower the volume of other apps if any of the active players is outputting audio.\n * - `auto`: The player will allow other apps to keep playing audio only when it is muted. On iOS it will always interrupt other apps when `showNowPlayingNotification` is `true` due to system requirements.\n * - `doNotMix`: The player will pause playback in other apps, even when it's muted.\n *\n * > On iOS, the Now Playing notification is dependent on the audio mode. If the audio mode is different from `doNotMix` or `auto` this feature will not work.\n */\nexport type AudioMixingMode = 'mixWithOthers' | 'duckOthers' | 'auto' | 'doNotMix';\n\nexport type SubtitleTrack = {\n /**\n * A string used by `expo-video` to identify the subtitle track.\n *\n * @platform android\n */\n id?: string;\n\n /**\n * Language of the subtitle track. For example, `en`, `pl`, `de`.\n */\n language: string;\n\n /**\n * Label of the subtitle track in the language of the device.\n */\n label: string;\n\n /**\n * Name of the subtitle track as specified in the media source.\n * @platform android\n * @platform ios\n */\n name?: string;\n\n /**\n * Indicates whether this is the default subtitle track.\n * @platform android\n * @platform ios\n */\n isDefault?: boolean;\n\n /**\n * Indicates whether this track should be auto-selected based on user preferences.\n * @platform android\n * @platform ios\n */\n autoSelect?: boolean;\n};\n\n/**\n * Specifies a VideoTrack loaded from a [`VideoSource`](#videosource).\n */\nexport type VideoTrack = {\n /**\n * The id of the video track.\n *\n * > This field is platform-specific and may return different depending on the operating system.\n */\n id: string;\n\n /**\n * The URL of the `VideoTrack` for HLS video sources. `null` for other source types.\n */\n url: string | null;\n\n /**\n * Size of the video track.\n */\n size: VideoSize;\n\n /**\n * MimeType of the video track or null if unknown.\n */\n mimeType: string | null;\n\n /**\n * Indicates whether the video track format is supported by the device.\n *\n * @platform android\n */\n isSupported: boolean;\n\n /**\n * Specifies the bitrate in bits per second. This is the peak bitrate if known, or else the average bitrate if known, or else null.\n *\n * @deprecated Use `peakBitrate` or `averageBitrate` instead.\n */\n bitrate: number | null;\n\n /**\n * Specifies the average bitrate in bits per second or null if the value is unknown.\n *\n */\n averageBitrate: number | null;\n\n /**\n * Specifies the average bitrate in bits per second or null if the value is unknown.\n */\n peakBitrate: number | null;\n\n /**\n * Specifies the frame rate of the video track in frames per second.\n */\n frameRate: number | null;\n\n /**\n * Specifies the video range of the video track.\n */\n videoRange: VideoRange;\n};\n\n/**\n * Specifies the size of a video track.\n */\nexport type VideoSize = {\n /**\n * Width of the video track in pixels.\n */\n width: number;\n /**\n * Height of the video track in pixels.\n */\n height: number;\n};\n\nexport type AudioTrack = {\n /**\n * A string used by expo-video to identify the audio track.\n * @platform android\n */\n id?: string;\n\n /**\n * Language of the audio track. For example, 'en', 'pl', 'de'.\n */\n language: string;\n\n /**\n * Label of the audio track in the language of the device.\n */\n label: string;\n\n /**\n * Name of the audio track as specified in the media source.\n * @platform android\n * @platform ios\n */\n name?: string;\n\n /**\n * Indicates whether this is the default audio track.\n * @platform android\n * @platform ios\n */\n isDefault?: boolean;\n\n /**\n * Indicates whether this track should be auto-selected based on user preferences.\n * @platform android\n * @platform ios\n */\n autoSelect?: boolean;\n};\n\n/**\n * Determines the time that the actual position seeked to may precede or exceed the requested seek position.\n * Larger tolerance will usually result in faster seeking.\n * This property affects the precision of setting the [`currentTime`](#currenttime) property and the [`seekBy`](#seekbyseconds) method, and on Android, it also affects the accuracy of the scrubber from the default native controls.\n *\n * > If you are trying to optimize for scrubbing (many frequent seeks), also see [`ScrubbingModeOptions`](#scrubbingmodeoptions-1).\n *\n * @platform android\n * @platform ios\n */\nexport type SeekTolerance = {\n /**\n * The maximum time that the actual position seeked to may precede the requested seek position, in seconds. Must be non-negative.\n * @default 0\n */\n toleranceBefore?: number;\n\n /**\n * The maximum time that the actual position seeked to may exceed the requested seek position, in seconds. Must be non-negative.\n * @default 0\n */\n toleranceAfter?: number;\n};\n\n/**\n * Defines scrubbing mode options used by a [`VideoPlayer`](#videoplayer).\n */\nexport type ScrubbingModeOptions = {\n /**\n * Whether the codec operating rate should be increased in scrubbing mode.\n *\n * You should only enable this when the player is receiving a large number of seeks in a short period of time. For less frequent seeks, fine-tuning the [`SeekTolerance`](#seektolerance-1) may be sufficient.\n *\n * On Android, the player may consume more resources in this mode, so it should only be used for short periods of time in response to user interaction (for example, dragging on a progress bar UI element).\n *\n * On Android, when `scrubbingModeEnabled` is `true`, the playback is suppressed. You should set this property back to `false` when the user interaction ends to allow the playback to resume.\n * For best results, on iOS you should pause the playback when scrubbing.\n *\n * > For best scrubbing performance, consider also increasing the seeking tolerance using the [`SeekTolerance`](#seektolerance-1) property.\n *\n * > Other scrubbing mode options will have no effect when this is `false`.\n * @default false\n * @platform android\n * @platform ios\n */\n scrubbingModeEnabled?: boolean;\n\n /**\n * Whether the codec operating rate should be increased in scrubbing mode.\n *\n * @platform android\n * @default true\n */\n increaseCodecOperatingRate?: boolean;\n\n /**\n * Sets whether ExoPlayer's dynamic scheduling should be enabled in scrubbing mode.\n * This can result in available output buffers being handled more quickly when seeking.\n *\n * @platform android\n * @default true\n */\n enableDynamicScheduling?: boolean;\n /**\n * Sets whether to use `MediaCodec.BUFFER_FLAG_DECODE_ONLY` in scrubbing mode.\n * When playback is using MediaCodec on API 34+, this flag can speed up seeking by signalling that the decoded output of buffers between the previous keyframe and the target frame is not needed by the player.\n *\n * @platform android\n * @default true\n */\n useDecodeOnlyFlag?: boolean;\n\n /**\n * Sets whether to avoid flushing the decoder (where possible) in scrubbing mode.\n * When `true`, avoids flushing the decoder when a new seek starts decoding from a key-frame in compatible content.\n *\n * @platform android\n * @default true\n */\n allowSkippingMediaCodecFlush?: boolean;\n};\n\n/**\n * Options to apply to the player builder before the native constructor is invoked\n * @platform android\n */\nexport type PlayerBuilderOptions = {\n /**\n * Seek backward increment in seconds.\n * Values will be clamped between 0.001 and 999 seconds.\n * @platform android\n */\n seekBackwardIncrement?: number;\n\n /**\n * Seek forward increment in seconds.\n * Values will be clamped between 0.001 and 999 seconds.\n * @platform android\n */\n seekForwardIncrement?: number;\n};\n\n/**\n * Specifies the dynamic range of the video content.\n * - `sdr`: Standard Dynamic Range video.\n * - `hlg`: Hybrid Log-Gamma - HDR backward-compatible with SDR displays\n * - `pq`: Perceptual Quantizer - Formats like HDR10 and Dolby Vision\n */\nexport type VideoRange = 'sdr' | 'hlg' | 'pq';\n"]}
|
|
1
|
+
{"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC","sourcesContent":["import { SharedObject } from 'expo';\n\nimport type { VideoPlayerEvents } from './VideoPlayerEvents.types';\nimport type { VideoThumbnail } from './VideoThumbnail';\n\n/**\n * A class that represents an instance of the video player.\n */\nexport declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {\n /**\n * Boolean value whether the player is currently playing.\n * > Use `play` and `pause` methods to control the playback.\n */\n readonly playing: boolean;\n\n /**\n * Determines whether the player should automatically replay after reaching the end of the video.\n * @default false\n */\n loop: boolean;\n\n /**\n * Determines whether the player should allow external playback.\n * @default true\n * @platform ios\n */\n allowsExternalPlayback: boolean;\n\n /**\n * Determines how the player will interact with other audio playing in the system.\n *\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n audioMixingMode: AudioMixingMode;\n\n /**\n * Boolean value whether the player is currently muted.\n * Setting this property to `true`/`false` will mute/unmute the player.\n * @default false\n */\n muted: boolean;\n\n /**\n * Float value indicating the current playback time in seconds.\n *\n * If the player is not yet playing, this value indicates the time position\n * at which playback will begin once the `play()` method is called.\n *\n * Setting `currentTime` to a new value seeks the player to the given time.\n * Check out the [`seekTolerance`](#seektolerance) property to configure the seeking precision.\n */\n currentTime: number;\n\n /**\n * The exact timestamp when the currently displayed video frame was sent from the server,\n * based on the `EXT-X-PROGRAM-DATE-TIME` tag in the livestream metadata.\n * If this metadata is missing, this property will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentLiveTimestamp: number | null;\n\n /**\n * Float value indicating the latency of the live stream in seconds.\n * If a livestream doesn't have the required metadata, this will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentOffsetFromLive: number | null;\n\n /**\n * Float value indicating the time offset from the live in seconds.\n * @platform ios\n */\n targetOffsetFromLive: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n */\n readonly duration: number;\n\n /**\n * Float value between `0` and `1.0` representing the current volume.\n * Muting the player doesn't affect the volume. In other words, when the player is muted, the volume is the same as\n * when unmuted. Similarly, setting the volume doesn't unmute the player.\n * @default 1.0\n */\n volume: number;\n\n /**\n * Boolean value indicating if the player should correct audio pitch when the playback speed changes.\n * @default true\n */\n preservesPitch: boolean;\n\n /**\n * Float value indicating the interval in seconds at which the player will emit the [`timeUpdate`](#videoplayerevents) event.\n * When the value is equal to `0`, the event will not be emitted.\n *\n * @default 0\n */\n timeUpdateEventInterval: number;\n\n /**\n * Float value between `0` and `16.0` indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean indicating if the player should keep the screen on while playing.\n *\n * > On Android, this property has an effect only when a [`VideoView`](#videoview) is visible. If you want to keep the screen awake at all times use [`expo-keep-awake`](./keep-awake/).\n *\n * @default true\n * @platform android\n * @platform ios\n */\n keepScreenOnWhilePlaying: boolean;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n */\n readonly isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n */\n readonly status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n *\n * > **Note**: On Android, `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)\n * > has to be `true` for the now playing notification to work.\n * @default false\n * @platform android\n * @platform ios\n */\n showNowPlayingNotification: boolean;\n\n /**\n * Determines whether the player should continue playing after the app enters the background.\n *\n * > **Note**: The `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)\n * > has to be `true` for the background playback to work.\n * @default false\n * @platform ios\n * @platform android\n */\n staysActiveInBackground: boolean;\n\n /**\n * Float value indicating how far the player has buffered the video in seconds.\n *\n * This value is 0 when the player has not buffered up to the current playback time.\n * When it's impossible to determine the buffer state (for example, when the player isn't playing any media), this value is -1.\n */\n readonly bufferedPosition: number;\n\n /**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * > You should provide a `BufferOptions` object when setting this property. Setting individual buffer properties is not supported.\n * @platform android\n * @platform ios\n */\n bufferOptions: BufferOptions;\n\n /**\n * Specifies the subtitle track which is currently displayed by the player. `null` when no subtitles are displayed.\n *\n * > To ensure a valid subtitle track, always assign one of the subtitle tracks from the [`availableSubtitleTracks`](#availablesubtitletracks) array.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n subtitleTrack: SubtitleTrack | null;\n\n /**\n * Specifies the audio track currently played by the player. `null` when no audio is played.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n audioTrack: AudioTrack | null;\n\n /**\n * An array of audio tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableAudioTracks: AudioTrack[];\n\n /**\n * An array of subtitle tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableSubtitleTracks: SubtitleTrack[];\n\n /**\n * Specifies the video track currently played by the player. `null` when no video is displayed.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n readonly videoTrack: VideoTrack | null;\n\n /**\n * An array of video tracks available for the current video.\n *\n * > On iOS, when using a HLS source, make sure that the uri contains `.m3u8` extension or that the [`contentType`](#contenttype) property of the [`VideoSource`](#videosource) has been set to `'hls'`. Otherwise, the video tracks will not be available.\n *\n * @platform android\n * @platform ios\n */\n readonly availableVideoTracks: VideoTrack[];\n\n /**\n * Indicates whether the player is currently playing back the media to an external device via AirPlay.\n *\n * @platform ios\n */\n readonly isExternalPlaybackActive: boolean;\n\n /**\n * Determines the time that the actual position seeked to may precede or exceed the requested seek position.\n *\n * This property affects the precision of setting the [`currentTime`](#currenttime) property and the [`seekBy`](#seekbyseconds) method, and on Android, it also affects the accuracy of the scrubber from the default native controls.\n *\n * By default, the player seeks to the exact requested time.\n *\n * > If you are trying to optimize for scrubbing (many frequent seeks), also see [`ScrubbingModeOptions`](#scrubbingmodeoptions-1).\n */\n seekTolerance: SeekTolerance;\n\n /**\n * Determines whether the scrubbing mode is enabled and what scrubbing optimizations should be enabled.\n *\n * > See [`SeekTolerance`](#seektolerance) to set the seeking tolerance, which can also affect the scrubbing performance.\n *\n */\n scrubbingModeOptions: ScrubbingModeOptions;\n\n /**\n * Initializes a new video player instance with the given source.\n *\n * @param source The source of the video to be played.\n * @param useSynchronousReplace Optional parameter, when `true` `source` from the first parameter will be loaded on the main thread.\n * @param playerBuilderOptions Options to apply to the player builder before the native constructor is invoked.\n * @hidden\n */\n constructor(\n source: VideoSource,\n useSynchronousReplace?: boolean,\n playerBuilderOptions?: PlayerBuilderOptions\n );\n\n /**\n * Resumes the player.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current source with a new one.\n *\n * > On iOS, this method loads the asset data synchronously on the UI thread and can block it for extended periods of time.\n * > Use `replaceAsync` to load the asset asynchronously and avoid UI lags.\n *\n * > This method will be deprecated in the future.\n */\n replace(source: VideoSource, disableWarning?: boolean): void;\n\n /**\n * Replaces the current source with a new one, while offloading loading of the asset to a different thread.\n *\n * > On Android and Web, this method is equivalent to `replace`.\n */\n replaceAsync(source: VideoSource): Promise<void>;\n\n /**\n * Seeks the playback by the given number of seconds. The time to which the player seeks may differ from the specified requested time for efficiency,\n * depending on the encoding and what is currently buffered by the player. Use this function to implement playback controls that seek by specific amount of time,\n * in which case, the actual time usually does not have to be precise. For frame accurate seeking, use the [`currentTime`](#currenttime) property.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n\n /**\n * Generates thumbnails from the currently played asset. The thumbnails are references to native images,\n * thus they can be used as a source of the `Image` component from `expo-image`.\n * @platform android\n * @platform ios\n */\n generateThumbnailsAsync(\n times: number | number[],\n options?: VideoThumbnailOptions\n ): Promise<VideoThumbnail[]>;\n}\n\n/**\n * Additional options for video thumbnails generation.\n */\nexport type VideoThumbnailOptions = {\n /**\n * If provided, the generated thumbnail will not exceed this width in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxWidth?: number;\n\n /**\n * If provided, the generated thumbnail will not exceed this height in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxHeight?: number;\n};\n\n/**\n * Describes the current status of the player.\n * - `idle`: The player is not playing or loading any videos.\n * - `loading`: The player is loading video data from the provided source\n * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.\n * - `error`: The player has encountered an error while loading or playing the video.\n */\nexport type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\nexport type VideoSource = string | number | null | VideoSourceObject;\n\nexport type VideoSourceObject = {\n /**\n * The URI of the video.\n *\n * On iOS, `PHAsset` URIs are supported, but can only be loaded using the [`replaceAsync`](#replaceasyncsource) method or the default [`VideoPlayer`](#videoplayer) constructor.\n *\n * This property is exclusive with the `assetId` property. When both are present, the `assetId` will be ignored.\n */\n uri?: string;\n\n /**\n * The asset ID of a local video asset, acquired with the `require` function.\n * This property is exclusive with the `uri` property. When both are present, the `assetId` will be ignored.\n */\n assetId?: number;\n\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\n\n /**\n * Specifies information which will be displayed in the now playing notification.\n * When undefined the player will display information contained in the video metadata.\n * @platform android\n * @platform ios\n */\n metadata?: VideoMetadata;\n\n /**\n * Specifies headers sent with the video request.\n * > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).\n * @platform android\n * @platform ios\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the player should use caching for the video.\n * > Due to platform limitations, the cache cannot be used with HLS video sources on iOS. Caching DRM-protected videos is not supported on Android and iOS.\n * @default false\n * @platform android\n * @platform ios\n */\n useCaching?: boolean;\n\n /**\n * Specifies the content type of the video source. When set to `'auto'`, the player will try to automatically determine the content type.\n *\n * You should use this property when playing HLS, SmoothStreaming or DASH videos from an uri, which does not contain a standardized extension for the corresponding media type.\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n contentType?: ContentType;\n\n /**\n * An array of external (sideloaded) subtitle files to attach to the video source.\n *\n * Use this to add subtitles to sources that don't declare their own subtitle tracks — most importantly\n * progressive `MP4` files, which (unlike `HLS`/`DASH` manifests) cannot advertise external subtitle renditions.\n * The sideloaded tracks are merged into the media and become available through\n * [`availableSubtitleTracks`](#availablesubtitletracks), so they can be selected with\n * [`subtitleTrack`](#subtitletrack) just like embedded tracks.\n *\n * > **Note (iOS):** sideloading is done by building an `AVMutableComposition`, which has platform limitations:\n * > the video must be a progressive (non-HLS) source without DRM, remote subtitle files are downloaded to a\n * > local cache first, and only `WebVTT` is natively supported (`SRT` files are converted to `WebVTT`\n * > automatically). Sideloading only runs on the asynchronous loading path (the default `VideoPlayer`\n * > constructor and [`replaceAsync`](#replaceasyncsource)), not on the synchronous `replace`.\n *\n * @platform android\n * @platform ios\n */\n subtitleTracks?: SubtitleSource[];\n};\n\n/**\n * Describes a single external (sideloaded) subtitle file that can be attached to a [`VideoSource`](#videosource).\n */\nexport type SubtitleSource = {\n /**\n * The URI of the subtitle file. Both local (`file://`) and remote (`http(s)://`) URIs are supported.\n */\n uri: string;\n\n /**\n * The BCP-47 / ISO 639 language code of the subtitle track, for example `en`, `pl`, `de`.\n * This value is used to identify and select the track and is shown in the native track selector.\n */\n language: string;\n\n /**\n * A human-readable label for the track, shown in the native subtitle selection UI.\n * When omitted, a label is derived from the `language`.\n */\n label?: string;\n\n /**\n * The format of the subtitle file.\n * When omitted, the format is inferred from the file extension (`.vtt` / `.srt`).\n *\n * - `vtt`: WebVTT subtitles (`text/vtt`).\n * - `srt`: SubRip subtitles (`application/x-subrip`). On iOS these are converted to WebVTT before sideloading.\n *\n * @default inferred from the uri extension\n */\n format?: 'vtt' | 'srt';\n\n /**\n * Whether this track should be marked as the default subtitle track.\n * @default false\n */\n default?: boolean;\n\n /**\n * How far above the bottom of the video this track renders, as a fraction of the video height (0–1).\n *\n * Useful when a video already carries burned-in (hardcoded) subtitles and the sideloaded ones would\n * otherwise be drawn on top of them.\n *\n * It sits here rather than on [`subtitleStyle`](#videoviewprops) because iOS can only apply it while\n * the subtitle is being merged into the video, which happens from the `VideoSource` alone — before any\n * `VideoView` has supplied a style. Changing it therefore takes effect the next time the source loads.\n *\n * > **Note:** cues that position themselves are left alone, so this does nothing for a WebVTT track\n * > whose cues already carry a `line:` setting.\n *\n * On **Android** use [`subtitleStyle.bottomOffset`](#subtitlestylebottomoffset) instead, which does the\n * same thing for every track at once and updates live.\n *\n * @platform ios\n */\n bottomOffset?: number;\n};\n\n/**\n * Contains information about any errors that the player encountered during the playback\n */\nexport type PlayerError = {\n message: string;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n * @platform android\n * @platform ios\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n * @platform android\n * @platform ios\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n * @platform android\n * @platform ios\n */\n artist?: string;\n /**\n * The uri of the video artwork.\n * @platform android\n * @platform ios\n */\n artwork?: string;\n};\n\n/**\n * Specifies which type of DRM to use:\n * - Android supports ClearKey, PlayReady and Widevine.\n * - iOS supports FairPlay.\n */\nexport type DRMType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';\n\n/**\n * Specifies DRM options which will be used by the player while loading the video.\n */\nexport type DRMOptions = {\n /**\n * Determines which type of DRM to use.\n */\n type: DRMType;\n\n /**\n * Determines the license server URL.\n */\n licenseServer: string;\n\n /**\n * Determines headers sent to the license server on license requests.\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the DRM is a multi-key DRM.\n * @platform android\n */\n multiKey?: boolean;\n\n /**\n * Specifies the content ID of the stream.\n * @platform ios\n */\n contentId?: string;\n\n /**\n * Specifies the certificate URL for the FairPlay DRM.\n * @platform ios\n */\n certificateUrl?: string;\n\n /**\n * Specifies the base64 encoded certificate data for the FairPlay DRM.\n * When this property is set, the `certificateUrl` property is ignored.\n * @platform ios\n */\n base64CertificateData?: string;\n};\n\n/**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * @platform android\n * @platform ios\n */\nexport type BufferOptions = {\n /**\n * The duration in seconds which determines how much media the player should buffer ahead of the current playback time.\n *\n * On iOS when set to `0` the player will automatically decide appropriate buffer duration.\n *\n * Equivalent to [`AVPlayerItem.preferredForwardBufferDuration`](https://developer.apple.com/documentation/avfoundation/avplayeritem/1643630-preferredforwardbufferduration).\n * @default Android: 20, iOS: 0\n * @platform android\n * @platform ios\n */\n readonly preferredForwardBufferDuration?: number;\n\n /**\n * A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling.\n *\n * Equivalent to [`AVPlayer.automaticallyWaitsToMinimizeStalling`](https://developer.apple.com/documentation/avfoundation/avplayer/1643482-automaticallywaitstominimizestal).\n * @default true\n * @platform ios\n */\n readonly waitsToMinimizeStalling?: boolean;\n\n /**\n * Minimum duration of the buffer in seconds required to continue playing after the player has been paused or started buffering.\n *\n * > This property will be ignored if `preferredForwardBufferDuration` is lower.\n * @default 2\n * @platform android\n */\n readonly minBufferForPlayback?: number;\n\n /**\n * The maximum number of bytes that the player can buffer from the network.\n * When 0 the player will automatically decide appropriate buffer size.\n *\n * @default 0\n * @platform android\n */\n readonly maxBufferBytes?: number | null;\n\n /**\n * A Boolean value which determines whether the player should prioritize time over size when buffering media.\n *\n * @default false\n * @platform android\n */\n readonly prioritizeTimeOverSizeThreshold?: boolean;\n};\n\n/**\n * Specifies the content type of the source.\n *\n * - `auto`: The player will automatically determine the content type of the video.\n * - `progressive`: The player will use progressive download content type. This is the default `ContentType` when the uri does not contain an extension.\n * - `hls`: The player will use HLS content type.\n * - `dash`: The player will use DASH content type (Android-only).\n * - `smoothStreaming`: The player will use SmoothStreaming content type (Android-only).\n *\n * @default `auto`\n */\nexport type ContentType = 'auto' | 'progressive' | 'hls' | 'dash' | 'smoothStreaming';\n\n/**\n * Specifies the audio mode that the player should use. Audio mode is set on per-app basis, if there are multiple players playing and\n * have different a `AudioMode` specified, the highest priority mode will be used. Priority order: 'doNotMix' > 'auto' > 'duckOthers' > 'mixWithOthers'.\n *\n * - `mixWithOthers`: The player will mix its audio output with other apps.\n * - `duckOthers`: The player will lower the volume of other apps if any of the active players is outputting audio.\n * - `auto`: The player will allow other apps to keep playing audio only when it is muted. On iOS it will always interrupt other apps when `showNowPlayingNotification` is `true` due to system requirements.\n * - `doNotMix`: The player will pause playback in other apps, even when it's muted.\n *\n * > On iOS, the Now Playing notification is dependent on the audio mode. If the audio mode is different from `doNotMix` or `auto` this feature will not work.\n */\nexport type AudioMixingMode = 'mixWithOthers' | 'duckOthers' | 'auto' | 'doNotMix';\n\nexport type SubtitleTrack = {\n /**\n * A string used by `expo-video` to identify the subtitle track.\n *\n * @platform android\n */\n id?: string;\n\n /**\n * Language of the subtitle track. For example, `en`, `pl`, `de`.\n */\n language: string;\n\n /**\n * Label of the subtitle track in the language of the device.\n */\n label: string;\n\n /**\n * Name of the subtitle track as specified in the media source.\n * @platform android\n * @platform ios\n */\n name?: string;\n\n /**\n * Indicates whether this is the default subtitle track.\n * @platform android\n * @platform ios\n */\n isDefault?: boolean;\n\n /**\n * Indicates whether this track should be auto-selected based on user preferences.\n * @platform android\n * @platform ios\n */\n autoSelect?: boolean;\n};\n\n/**\n * Specifies a VideoTrack loaded from a [`VideoSource`](#videosource).\n */\nexport type VideoTrack = {\n /**\n * The id of the video track.\n *\n * > This field is platform-specific and may return different depending on the operating system.\n */\n id: string;\n\n /**\n * The URL of the `VideoTrack` for HLS video sources. `null` for other source types.\n */\n url: string | null;\n\n /**\n * Size of the video track.\n */\n size: VideoSize;\n\n /**\n * MimeType of the video track or null if unknown.\n */\n mimeType: string | null;\n\n /**\n * Indicates whether the video track format is supported by the device.\n *\n * @platform android\n */\n isSupported: boolean;\n\n /**\n * Specifies the bitrate in bits per second. This is the peak bitrate if known, or else the average bitrate if known, or else null.\n *\n * @deprecated Use `peakBitrate` or `averageBitrate` instead.\n */\n bitrate: number | null;\n\n /**\n * Specifies the average bitrate in bits per second or null if the value is unknown.\n *\n */\n averageBitrate: number | null;\n\n /**\n * Specifies the average bitrate in bits per second or null if the value is unknown.\n */\n peakBitrate: number | null;\n\n /**\n * Specifies the frame rate of the video track in frames per second.\n */\n frameRate: number | null;\n\n /**\n * Specifies the video range of the video track.\n */\n videoRange: VideoRange;\n};\n\n/**\n * Specifies the size of a video track.\n */\nexport type VideoSize = {\n /**\n * Width of the video track in pixels.\n */\n width: number;\n /**\n * Height of the video track in pixels.\n */\n height: number;\n};\n\nexport type AudioTrack = {\n /**\n * A string used by expo-video to identify the audio track.\n * @platform android\n */\n id?: string;\n\n /**\n * Language of the audio track. For example, 'en', 'pl', 'de'.\n */\n language: string;\n\n /**\n * Label of the audio track in the language of the device.\n */\n label: string;\n\n /**\n * Name of the audio track as specified in the media source.\n * @platform android\n * @platform ios\n */\n name?: string;\n\n /**\n * Indicates whether this is the default audio track.\n * @platform android\n * @platform ios\n */\n isDefault?: boolean;\n\n /**\n * Indicates whether this track should be auto-selected based on user preferences.\n * @platform android\n * @platform ios\n */\n autoSelect?: boolean;\n};\n\n/**\n * Determines the time that the actual position seeked to may precede or exceed the requested seek position.\n * Larger tolerance will usually result in faster seeking.\n * This property affects the precision of setting the [`currentTime`](#currenttime) property and the [`seekBy`](#seekbyseconds) method, and on Android, it also affects the accuracy of the scrubber from the default native controls.\n *\n * > If you are trying to optimize for scrubbing (many frequent seeks), also see [`ScrubbingModeOptions`](#scrubbingmodeoptions-1).\n *\n * @platform android\n * @platform ios\n */\nexport type SeekTolerance = {\n /**\n * The maximum time that the actual position seeked to may precede the requested seek position, in seconds. Must be non-negative.\n * @default 0\n */\n toleranceBefore?: number;\n\n /**\n * The maximum time that the actual position seeked to may exceed the requested seek position, in seconds. Must be non-negative.\n * @default 0\n */\n toleranceAfter?: number;\n};\n\n/**\n * Defines scrubbing mode options used by a [`VideoPlayer`](#videoplayer).\n */\nexport type ScrubbingModeOptions = {\n /**\n * Whether the codec operating rate should be increased in scrubbing mode.\n *\n * You should only enable this when the player is receiving a large number of seeks in a short period of time. For less frequent seeks, fine-tuning the [`SeekTolerance`](#seektolerance-1) may be sufficient.\n *\n * On Android, the player may consume more resources in this mode, so it should only be used for short periods of time in response to user interaction (for example, dragging on a progress bar UI element).\n *\n * On Android, when `scrubbingModeEnabled` is `true`, the playback is suppressed. You should set this property back to `false` when the user interaction ends to allow the playback to resume.\n * For best results, on iOS you should pause the playback when scrubbing.\n *\n * > For best scrubbing performance, consider also increasing the seeking tolerance using the [`SeekTolerance`](#seektolerance-1) property.\n *\n * > Other scrubbing mode options will have no effect when this is `false`.\n * @default false\n * @platform android\n * @platform ios\n */\n scrubbingModeEnabled?: boolean;\n\n /**\n * Whether the codec operating rate should be increased in scrubbing mode.\n *\n * @platform android\n * @default true\n */\n increaseCodecOperatingRate?: boolean;\n\n /**\n * Sets whether ExoPlayer's dynamic scheduling should be enabled in scrubbing mode.\n * This can result in available output buffers being handled more quickly when seeking.\n *\n * @platform android\n * @default true\n */\n enableDynamicScheduling?: boolean;\n /**\n * Sets whether to use `MediaCodec.BUFFER_FLAG_DECODE_ONLY` in scrubbing mode.\n * When playback is using MediaCodec on API 34+, this flag can speed up seeking by signalling that the decoded output of buffers between the previous keyframe and the target frame is not needed by the player.\n *\n * @platform android\n * @default true\n */\n useDecodeOnlyFlag?: boolean;\n\n /**\n * Sets whether to avoid flushing the decoder (where possible) in scrubbing mode.\n * When `true`, avoids flushing the decoder when a new seek starts decoding from a key-frame in compatible content.\n *\n * @platform android\n * @default true\n */\n allowSkippingMediaCodecFlush?: boolean;\n};\n\n/**\n * Options to apply to the player builder before the native constructor is invoked\n * @platform android\n */\nexport type PlayerBuilderOptions = {\n /**\n * Seek backward increment in seconds.\n * Values will be clamped between 0.001 and 999 seconds.\n * @platform android\n */\n seekBackwardIncrement?: number;\n\n /**\n * Seek forward increment in seconds.\n * Values will be clamped between 0.001 and 999 seconds.\n * @platform android\n */\n seekForwardIncrement?: number;\n};\n\n/**\n * Specifies the dynamic range of the video content.\n * - `sdr`: Standard Dynamic Range video.\n * - `hlg`: Hybrid Log-Gamma - HDR backward-compatible with SDR displays\n * - `pq`: Perceptual Quantizer - Formats like HDR10 and Dolby Vision\n */\nexport type VideoRange = 'sdr' | 'hlg' | 'pq';\n"]}
|
|
@@ -256,7 +256,14 @@ export type SubtitleStyle = {
|
|
|
256
256
|
*/
|
|
257
257
|
edgeColor?: string;
|
|
258
258
|
/**
|
|
259
|
-
* The distance of the subtitles from the bottom of the video, expressed as a fraction
|
|
259
|
+
* The distance of the subtitles from the bottom of the video, expressed as a fraction (`0`–`1`).
|
|
260
|
+
*
|
|
261
|
+
* The fraction is of the content frame, which with the default `contentFit="contain"` is the displayed
|
|
262
|
+
* picture rather than the whole view — so one value keeps captions at the same height relative to the
|
|
263
|
+
* image across screen sizes, pixel densities, orientations and letterboxed sources, with no conversion
|
|
264
|
+
* needed. With `contentFit="cover"` or `"fill"` the content frame is the view.
|
|
265
|
+
*
|
|
266
|
+
* @default 0.08
|
|
260
267
|
* @platform android
|
|
261
268
|
*/
|
|
262
269
|
bottomOffset?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoView.types.d.ts","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAE5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B;;OAEG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE/C;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IAErC;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpC;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;OAUG;IACH,mCAAmC,CAAC,EAAE,OAAO,CAAC;IAE9C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAE/B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAE9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEhC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;IAE9C;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1F;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB
|
|
1
|
+
{"version":3,"file":"VideoView.types.d.ts","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoView.types.js","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ViewProps } from 'react-native';\n\nimport type { VideoPlayer } from './VideoPlayer.types';\n\n/**\n * Describes how a video should be scaled to fit in a container.\n * - `contain`: The video maintains its aspect ratio and fits inside the container, with possible letterboxing/pillarboxing.\n * - `cover`: The video maintains its aspect ratio and covers the entire container, potentially cropping some portions.\n * - `fill`: The video stretches/squeezes to completely fill the container, potentially causing distortion.\n */\nexport type VideoContentFit = 'contain' | 'cover' | 'fill';\n\n/**\n * Describes the type of the surface used to render the video.\n * - `surfaceView`: Uses the `SurfaceView` to render the video. This value should be used in the majority of cases. Provides significantly lower power consumption, better performance, and more features.\n * - `textureView`: Uses the `TextureView` to render the video. Should be used in cases where the SurfaceView is not supported or causes issues (for example, overlapping video views).\n *\n * You can learn more about surface types in the official [ExoPlayer documentation](https://developer.android.com/media/media3/ui/playerview#surfacetype).\n * @platform android\n */\nexport type SurfaceType = 'textureView' | 'surfaceView';\n\nexport interface VideoViewProps extends ViewProps {\n /**\n * A video player instance. Use [`useVideoPlayer()`](#usevideoplayersource-setup) hook to create one.\n */\n player?: VideoPlayer | null;\n\n /**\n * Determines whether native controls should be displayed or not.\n *\n * > **Note**: Due to platform limitations, the native controls are always enabled in fullscreen mode.\n * @default true\n */\n nativeControls?: boolean;\n\n /**\n * Describes how the video should be scaled to fit in the container.\n * Options are `'contain'`, `'cover'`, and `'fill'`.\n * @default 'contain'\n */\n contentFit?: VideoContentFit;\n\n /**\n * Determines the fullscreen mode options.\n */\n fullscreenOptions?: FullscreenOptions;\n\n /**\n * Determines whether the timecodes should be displayed or not.\n * @default true\n * @platform ios\n */\n showsTimecodes?: boolean;\n\n /**\n * Determines whether the player allows the user to skip media content.\n * @default false\n * @platform android\n * @platform ios\n */\n requiresLinearPlayback?: boolean;\n\n /**\n * Configuration for controlling the visibility of player control buttons.\n * @platform android\n */\n buttonOptions?: ButtonOptions;\n\n /**\n * Customizes the appearance of the rendered subtitles/captions (text color, background, font, edge, and position).\n *\n * The style is applied to whatever subtitle track is currently displayed, regardless of whether it comes\n * from an embedded (`HLS`/`DASH`) track or an external one attached through\n * [`VideoSource.subtitleTracks`](#videosourcesubtitletracks).\n *\n * > **Note (iOS):** styling relies on `AVPlayerItem.textStyleRules`, which only affects `WebVTT` captions that\n * > the system renders itself. The OS-level *Settings → Accessibility → Subtitles & Captioning* preferences\n * > take precedence over these values when the user has enabled them.\n *\n * @platform android\n * @platform ios\n */\n subtitleStyle?: SubtitleStyle;\n\n /**\n * Determines the type of the surface used to render the video.\n * > This prop should not be changed at runtime.\n * @default 'surfaceView'\n * @platform android\n */\n surfaceType?: SurfaceType;\n\n /**\n * Determines the position offset of the video inside the container.\n * @default { dx: 0, dy: 0 }\n * @platform ios\n */\n contentPosition?: { dx?: number; dy?: number };\n\n /**\n * A callback to call after the video player enters Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStart?: () => void;\n\n /**\n * A callback to call after the video player exits Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStop?: () => void;\n\n /**\n * Determines whether the player allows Picture in Picture (PiP) mode.\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n * @platform android\n * @platform ios\n * @platform web\n */\n allowsPictureInPicture?: boolean;\n\n /**\n * Determines whether a video should be played \"inline\", that is, within the element's playback area.\n * @platform web\n */\n playsInline?: boolean;\n\n /**\n * Determines whether the player should start Picture in Picture (PiP) automatically when the app is in the background.\n * > **Note:** Only one player can be in Picture in Picture (PiP) mode at a time.\n *\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n *\n * @default false\n * @platform android 12+\n * @platform ios\n */\n startsPictureInPictureAutomatically?: boolean;\n\n /**\n * Specifies whether to perform video frame analysis (Live Text in videos).\n * Check official [Apple documentation](https://developer.apple.com/documentation/avkit/avplayerviewcontroller/allowsvideoframeanalysis) for more details.\n * @default true\n * @platform ios 16.0+\n */\n allowsVideoFrameAnalysis?: boolean;\n\n /**\n * A callback to call after the video player enters fullscreen mode.\n */\n onFullscreenEnter?: () => void;\n\n /**\n * A callback to call after the video player exits fullscreen mode.\n */\n onFullscreenExit?: () => void;\n\n /**\n * A callback to call after the mounted `VideoPlayer` has rendered the first frame into the `VideoView`.\n * This event can be used to hide any cover images that conceal the initial loading of the player.\n * > **Note:** This event may also be called during playback when the current video track changes (for example when the player switches video quality).\n */\n onFirstFrameRender?: () => void;\n\n /**\n * Determines whether the player should use the default ExoPlayer shutter that covers the `VideoView` before the first video frame is rendered.\n * Setting this property to `false` makes the Android behavior the same as iOS.\n *\n * @platform android\n * @default false\n */\n useExoShutter?: boolean;\n\n /**\n * Determines the [cross origin policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/crossorigin) used by the underlying native view on web.\n * If `undefined` (default), does not use CORS at all. If set to `'anonymous'`, the video will be loaded with CORS enabled.\n * Note that some videos may not play if CORS is enabled, depending on the CDN settings.\n * If you encounter issues, consider adjusting the `crossOrigin` property.\n *\n *\n * @platform web\n * @default undefined\n */\n crossOrigin?: 'anonymous' | 'use-credentials';\n\n /**\n * Use Audio Nodes for sound playback. When the same player is playing in multiple video views the audio won't increase in volume\n * as the number of players increases.\n *\n * > **Note**: This property is experimental, when enabled it is known to break audio for some sources. Do not change this property at runtime.\n *\n * @experimental\n * @default false\n * @platform web\n */\n useAudioNodePlayback?: boolean;\n}\n\n/**\n * Describes the edge (outline) effect drawn around subtitle text.\n * - `none`: No edge effect.\n * - `outline`: A solid outline around each glyph.\n * - `dropShadow`: A drop shadow behind the text.\n * - `raised`: The text appears raised from the background.\n * - `depressed`: The text appears pressed into the background.\n */\nexport type SubtitleEdgeType = 'none' | 'outline' | 'dropShadow' | 'raised' | 'depressed';\n\n/**\n * Describes the visual style applied to the rendered subtitles/captions.\n *\n * Colors accept a hex string in the form `#RGB`, `#RRGGBB`, or `#RRGGBBAA` (an alpha suffix controls opacity).\n *\n * @platform android\n * @platform ios\n */\nexport type SubtitleStyle = {\n /**\n * The color of the subtitle text.\n * @default '#FFFFFF'\n */\n textColor?: string;\n\n /**\n * The background color drawn directly behind the text glyphs (the caption \"box\").\n * Use an alpha suffix (for example `#000000A0`) for a translucent box.\n * @default 'transparent'\n */\n backgroundColor?: string;\n\n /**\n * The background color of the whole cue region (the \"window\" behind the whole line).\n * @default 'transparent'\n */\n windowColor?: string;\n\n /**\n * The subtitle font size.\n *\n * > The unit differs per platform: on Android this is an absolute size in `sp`; on iOS it is a percentage\n * > relative to the default caption size (`100` = default, `150` = 1.5×).\n *\n * @platform android\n * @platform ios\n */\n fontSize?: number;\n\n /**\n * The name of the font family used to render the subtitles.\n *\n * Custom fonts bundled with the app are supported, using the same name you would pass to a\n * `<Text>` component's `fontFamily`:\n * - On Android the font is looked up as an Android font resource (`res/font`, where `expo-font`'s\n * config plugin places fonts) and then through React Native's font manager, which covers the\n * `assets/fonts/<name>.ttf` convention and fonts registered at runtime.\n * - On iOS the font must be registered with the system, which is what bundling it through\n * `expo-font`'s config plugin (`UIAppFonts`) does.\n *\n * The built-in system families always work — for example `sans-serif`, `sans-serif-medium`,\n * `serif`, `monospace` and `cursive` on Android.\n *\n * > A name that matches no font falls back to the default typeface silently: neither platform\n * > reports an unresolved font family, so double-check the spelling if nothing changes.\n */\n fontFamily?: string;\n\n /**\n * Whether the subtitle text should be rendered in bold.\n * @default false\n */\n bold?: boolean;\n\n /**\n * The edge (outline) effect drawn around the text.\n * @default 'none'\n */\n edgeType?: SubtitleEdgeType;\n\n /**\n * The color of the edge effect. Only has an effect when `edgeType` is not `'none'`.\n */\n edgeColor?: string;\n\n /**\n * The distance of the subtitles from the bottom of the video, expressed as a fraction of the view height (`0`–`1`).\n * @platform android\n */\n bottomOffset?: number;\n\n /**\n * Corner radius, in dp, of the box drawn behind the caption text by [`backgroundColor`](#subtitlestylebackgroundcolor).\n *\n * A box is drawn per rendered line, so a caption that wraps onto two lines gets two rounded boxes, the\n * way VLC and most desktop players draw them. Has no effect unless `backgroundColor` is set and not\n * fully transparent.\n *\n * > **Note:** [`windowColor`](#subtitlestylewindowcolor) is not affected — the caption *window* stays\n * > rectangular. Rounding applies to the box that hugs the text.\n *\n * @default 0 (square corners)\n * @platform android\n */\n backgroundRadius?: number;\n\n /**\n * Whether the inline formatting carried by the subtitle track itself should be kept.\n *\n * Subtitle formats bring their own styling — `SubRip`'s `<b>`, `<i>`, `<u>` and `<font color>` tags, and the\n * richer per-cue styling of embedded `WebVTT`/`TTML` tracks. By default all of it is stripped, so the other\n * properties of this object are the only thing that decides how captions look. Set this to `true` to let a\n * track style its own text.\n *\n * The remaining `SubtitleStyle` properties still apply underneath, and `fontSize` keeps overriding any size\n * the track asks for. Only the properties a cue explicitly sets are affected — an unstyled cue looks the same\n * either way.\n *\n * > **Note:** a track that specifies its own colors will override [`textColor`](#subtitlestyletextcolor) and\n * > [`windowColor`](#subtitlestylewindowcolor) for the cues that specify them.\n *\n * @default false\n * @platform android\n */\n applyEmbeddedStyles?: boolean;\n};\n\n/**\n * Configuration for controlling the visibility of player control buttons.\n *\n * > The fullscreen button should be controlled with [`fullscreenOptions.enable`](#fullscreenoptions).\n *\n * @platform android\n */\nexport type ButtonOptions = {\n /**\n * Whether to show the next button.\n * @default false\n */\n showNext?: boolean;\n /**\n * Whether to show the previous button.\n * @default false\n */\n showPrevious?: boolean;\n /**\n * Whether to show the seek forward button.\n * @default true\n */\n showSeekForward?: boolean;\n /**\n * Whether to show the seek backward button.\n * @default true\n */\n showSeekBackward?: boolean;\n /**\n * Whether to show the subtitles button.\n * - `true`: Button is always visible\n * - `false`: Button is never visible\n * - `undefined`: Button is visible only when subtitles are available (default behavior)\n * @default undefined\n */\n showSubtitles?: boolean | null;\n /**\n * Whether to show the settings button.\n * @default true\n */\n showSettings?: boolean;\n /**\n * Whether to show the play/pause button.\n * @default true\n */\n showPlayPause?: boolean;\n /**\n * Whether to show the bottom control bar (containing time, progress bar, and buttons).\n * When set to `false`, the entire bottom bar including the progress bar will be hidden.\n *\n * > **Note**: The bottom bar is always visible in fullscreen mode to allow users to exit fullscreen.\n *\n * @default true\n */\n showBottomBar?: boolean;\n};\n\n/**\n * Describes the orientation of the video in fullscreen mode. Available values are:\n * - `default`: The video is displayed in any of the available device rotations.\n * - `portrait`: The video is displayed in one of two available portrait orientations and rotates between them.\n * - `portraitUp`: The video is displayed in the portrait orientation - the notch of the phone points upwards.\n * - `portraitDown`: The video is displayed in the portrait orientation - the notch of the phone points downwards.\n * - `landscape`: The video is displayed in one of two available landscape orientations and rotates between them.\n * - `landscapeLeft`: The video is displayed in the left landscape orientation - the notch of the phone is in the left palm of the user.\n * - `landscapeRight`: The video is displayed in the right landscape orientation - the notch of the phone is in the right palm of the user.\n */\nexport type FullscreenOrientation =\n | 'default'\n | 'portrait'\n | 'portraitUp'\n | 'portraitDown'\n | 'landscape'\n | 'landscapeLeft'\n | 'landscapeRight';\n\n/**\n * Determines whether the player keeps fullscreen when Picture in Picture (PiP) stops.\n * Only has an effect if the player was in fullscreen when PiP started.\n * - `'always'`: Always re-enter fullscreen when PiP stops.\n * - `'autoEnter'`: Re-enter fullscreen only when PiP was started automatically by the app going to the background.\n * - `'never'`: Do not re-enter fullscreen when PiP stops.\n */\nexport type KeepFullscreenOnPiPStopBehavior = 'always' | 'autoEnter' | 'never';\n\n/**\n * Describes the options for fullscreen video mode.\n */\nexport type FullscreenOptions = {\n /**\n * Specifies whether the fullscreen mode should be available to the user. When `false`, the fullscreen button will be hidden in the player.\n * @default true\n */\n enable: boolean;\n /**\n * Specifies the orientation of the video in fullscreen mode.\n * @default 'default'\n * @platform android\n * @platform ios\n */\n orientation?: FullscreenOrientation;\n /**\n * Specifies whether the app should exit fullscreen mode when the device is rotated to a different orientation than the one specified in the `orientation` prop.\n * For example, if the `orientation` prop is set to `landscape` and the device is rotated to `portrait`, the app will exit fullscreen mode.\n *\n * > This prop will have no effect if the `orientation` prop is set to `default`.\n * > The `VideoView` will never auto-exit fullscreen when the device auto-rotate feature has been disabled in settings.\n *\n * @default false\n * @platform android\n * @platform ios\n */\n autoExitOnRotate?: boolean;\n\n /**\n * Determines whether the player keeps fullscreen when Picture in Picture (PiP) stops.\n * Only has an effect if the player was in fullscreen when PiP started.\n *\n * @default 'autoEnter'\n * @platform ios\n */\n keepFullscreenOnPiPStop?: KeepFullscreenOnPiPStopBehavior;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"VideoView.types.js","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ViewProps } from 'react-native';\n\nimport type { VideoPlayer } from './VideoPlayer.types';\n\n/**\n * Describes how a video should be scaled to fit in a container.\n * - `contain`: The video maintains its aspect ratio and fits inside the container, with possible letterboxing/pillarboxing.\n * - `cover`: The video maintains its aspect ratio and covers the entire container, potentially cropping some portions.\n * - `fill`: The video stretches/squeezes to completely fill the container, potentially causing distortion.\n */\nexport type VideoContentFit = 'contain' | 'cover' | 'fill';\n\n/**\n * Describes the type of the surface used to render the video.\n * - `surfaceView`: Uses the `SurfaceView` to render the video. This value should be used in the majority of cases. Provides significantly lower power consumption, better performance, and more features.\n * - `textureView`: Uses the `TextureView` to render the video. Should be used in cases where the SurfaceView is not supported or causes issues (for example, overlapping video views).\n *\n * You can learn more about surface types in the official [ExoPlayer documentation](https://developer.android.com/media/media3/ui/playerview#surfacetype).\n * @platform android\n */\nexport type SurfaceType = 'textureView' | 'surfaceView';\n\nexport interface VideoViewProps extends ViewProps {\n /**\n * A video player instance. Use [`useVideoPlayer()`](#usevideoplayersource-setup) hook to create one.\n */\n player?: VideoPlayer | null;\n\n /**\n * Determines whether native controls should be displayed or not.\n *\n * > **Note**: Due to platform limitations, the native controls are always enabled in fullscreen mode.\n * @default true\n */\n nativeControls?: boolean;\n\n /**\n * Describes how the video should be scaled to fit in the container.\n * Options are `'contain'`, `'cover'`, and `'fill'`.\n * @default 'contain'\n */\n contentFit?: VideoContentFit;\n\n /**\n * Determines the fullscreen mode options.\n */\n fullscreenOptions?: FullscreenOptions;\n\n /**\n * Determines whether the timecodes should be displayed or not.\n * @default true\n * @platform ios\n */\n showsTimecodes?: boolean;\n\n /**\n * Determines whether the player allows the user to skip media content.\n * @default false\n * @platform android\n * @platform ios\n */\n requiresLinearPlayback?: boolean;\n\n /**\n * Configuration for controlling the visibility of player control buttons.\n * @platform android\n */\n buttonOptions?: ButtonOptions;\n\n /**\n * Customizes the appearance of the rendered subtitles/captions (text color, background, font, edge, and position).\n *\n * The style is applied to whatever subtitle track is currently displayed, regardless of whether it comes\n * from an embedded (`HLS`/`DASH`) track or an external one attached through\n * [`VideoSource.subtitleTracks`](#videosourcesubtitletracks).\n *\n * > **Note (iOS):** styling relies on `AVPlayerItem.textStyleRules`, which only affects `WebVTT` captions that\n * > the system renders itself. The OS-level *Settings → Accessibility → Subtitles & Captioning* preferences\n * > take precedence over these values when the user has enabled them.\n *\n * @platform android\n * @platform ios\n */\n subtitleStyle?: SubtitleStyle;\n\n /**\n * Determines the type of the surface used to render the video.\n * > This prop should not be changed at runtime.\n * @default 'surfaceView'\n * @platform android\n */\n surfaceType?: SurfaceType;\n\n /**\n * Determines the position offset of the video inside the container.\n * @default { dx: 0, dy: 0 }\n * @platform ios\n */\n contentPosition?: { dx?: number; dy?: number };\n\n /**\n * A callback to call after the video player enters Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStart?: () => void;\n\n /**\n * A callback to call after the video player exits Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStop?: () => void;\n\n /**\n * Determines whether the player allows Picture in Picture (PiP) mode.\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n * @platform android\n * @platform ios\n * @platform web\n */\n allowsPictureInPicture?: boolean;\n\n /**\n * Determines whether a video should be played \"inline\", that is, within the element's playback area.\n * @platform web\n */\n playsInline?: boolean;\n\n /**\n * Determines whether the player should start Picture in Picture (PiP) automatically when the app is in the background.\n * > **Note:** Only one player can be in Picture in Picture (PiP) mode at a time.\n *\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n *\n * @default false\n * @platform android 12+\n * @platform ios\n */\n startsPictureInPictureAutomatically?: boolean;\n\n /**\n * Specifies whether to perform video frame analysis (Live Text in videos).\n * Check official [Apple documentation](https://developer.apple.com/documentation/avkit/avplayerviewcontroller/allowsvideoframeanalysis) for more details.\n * @default true\n * @platform ios 16.0+\n */\n allowsVideoFrameAnalysis?: boolean;\n\n /**\n * A callback to call after the video player enters fullscreen mode.\n */\n onFullscreenEnter?: () => void;\n\n /**\n * A callback to call after the video player exits fullscreen mode.\n */\n onFullscreenExit?: () => void;\n\n /**\n * A callback to call after the mounted `VideoPlayer` has rendered the first frame into the `VideoView`.\n * This event can be used to hide any cover images that conceal the initial loading of the player.\n * > **Note:** This event may also be called during playback when the current video track changes (for example when the player switches video quality).\n */\n onFirstFrameRender?: () => void;\n\n /**\n * Determines whether the player should use the default ExoPlayer shutter that covers the `VideoView` before the first video frame is rendered.\n * Setting this property to `false` makes the Android behavior the same as iOS.\n *\n * @platform android\n * @default false\n */\n useExoShutter?: boolean;\n\n /**\n * Determines the [cross origin policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/crossorigin) used by the underlying native view on web.\n * If `undefined` (default), does not use CORS at all. If set to `'anonymous'`, the video will be loaded with CORS enabled.\n * Note that some videos may not play if CORS is enabled, depending on the CDN settings.\n * If you encounter issues, consider adjusting the `crossOrigin` property.\n *\n *\n * @platform web\n * @default undefined\n */\n crossOrigin?: 'anonymous' | 'use-credentials';\n\n /**\n * Use Audio Nodes for sound playback. When the same player is playing in multiple video views the audio won't increase in volume\n * as the number of players increases.\n *\n * > **Note**: This property is experimental, when enabled it is known to break audio for some sources. Do not change this property at runtime.\n *\n * @experimental\n * @default false\n * @platform web\n */\n useAudioNodePlayback?: boolean;\n}\n\n/**\n * Describes the edge (outline) effect drawn around subtitle text.\n * - `none`: No edge effect.\n * - `outline`: A solid outline around each glyph.\n * - `dropShadow`: A drop shadow behind the text.\n * - `raised`: The text appears raised from the background.\n * - `depressed`: The text appears pressed into the background.\n */\nexport type SubtitleEdgeType = 'none' | 'outline' | 'dropShadow' | 'raised' | 'depressed';\n\n/**\n * Describes the visual style applied to the rendered subtitles/captions.\n *\n * Colors accept a hex string in the form `#RGB`, `#RRGGBB`, or `#RRGGBBAA` (an alpha suffix controls opacity).\n *\n * @platform android\n * @platform ios\n */\nexport type SubtitleStyle = {\n /**\n * The color of the subtitle text.\n * @default '#FFFFFF'\n */\n textColor?: string;\n\n /**\n * The background color drawn directly behind the text glyphs (the caption \"box\").\n * Use an alpha suffix (for example `#000000A0`) for a translucent box.\n * @default 'transparent'\n */\n backgroundColor?: string;\n\n /**\n * The background color of the whole cue region (the \"window\" behind the whole line).\n * @default 'transparent'\n */\n windowColor?: string;\n\n /**\n * The subtitle font size.\n *\n * > The unit differs per platform: on Android this is an absolute size in `sp`; on iOS it is a percentage\n * > relative to the default caption size (`100` = default, `150` = 1.5×).\n *\n * @platform android\n * @platform ios\n */\n fontSize?: number;\n\n /**\n * The name of the font family used to render the subtitles.\n *\n * Custom fonts bundled with the app are supported, using the same name you would pass to a\n * `<Text>` component's `fontFamily`:\n * - On Android the font is looked up as an Android font resource (`res/font`, where `expo-font`'s\n * config plugin places fonts) and then through React Native's font manager, which covers the\n * `assets/fonts/<name>.ttf` convention and fonts registered at runtime.\n * - On iOS the font must be registered with the system, which is what bundling it through\n * `expo-font`'s config plugin (`UIAppFonts`) does.\n *\n * The built-in system families always work — for example `sans-serif`, `sans-serif-medium`,\n * `serif`, `monospace` and `cursive` on Android.\n *\n * > A name that matches no font falls back to the default typeface silently: neither platform\n * > reports an unresolved font family, so double-check the spelling if nothing changes.\n */\n fontFamily?: string;\n\n /**\n * Whether the subtitle text should be rendered in bold.\n * @default false\n */\n bold?: boolean;\n\n /**\n * The edge (outline) effect drawn around the text.\n * @default 'none'\n */\n edgeType?: SubtitleEdgeType;\n\n /**\n * The color of the edge effect. Only has an effect when `edgeType` is not `'none'`.\n */\n edgeColor?: string;\n\n /**\n * The distance of the subtitles from the bottom of the video, expressed as a fraction (`0`–`1`).\n *\n * The fraction is of the content frame, which with the default `contentFit=\"contain\"` is the displayed\n * picture rather than the whole view — so one value keeps captions at the same height relative to the\n * image across screen sizes, pixel densities, orientations and letterboxed sources, with no conversion\n * needed. With `contentFit=\"cover\"` or `\"fill\"` the content frame is the view.\n *\n * @default 0.08\n * @platform android\n */\n bottomOffset?: number;\n\n /**\n * Corner radius, in dp, of the box drawn behind the caption text by [`backgroundColor`](#subtitlestylebackgroundcolor).\n *\n * A box is drawn per rendered line, so a caption that wraps onto two lines gets two rounded boxes, the\n * way VLC and most desktop players draw them. Has no effect unless `backgroundColor` is set and not\n * fully transparent.\n *\n * > **Note:** [`windowColor`](#subtitlestylewindowcolor) is not affected — the caption *window* stays\n * > rectangular. Rounding applies to the box that hugs the text.\n *\n * @default 0 (square corners)\n * @platform android\n */\n backgroundRadius?: number;\n\n /**\n * Whether the inline formatting carried by the subtitle track itself should be kept.\n *\n * Subtitle formats bring their own styling — `SubRip`'s `<b>`, `<i>`, `<u>` and `<font color>` tags, and the\n * richer per-cue styling of embedded `WebVTT`/`TTML` tracks. By default all of it is stripped, so the other\n * properties of this object are the only thing that decides how captions look. Set this to `true` to let a\n * track style its own text.\n *\n * The remaining `SubtitleStyle` properties still apply underneath, and `fontSize` keeps overriding any size\n * the track asks for. Only the properties a cue explicitly sets are affected — an unstyled cue looks the same\n * either way.\n *\n * > **Note:** a track that specifies its own colors will override [`textColor`](#subtitlestyletextcolor) and\n * > [`windowColor`](#subtitlestylewindowcolor) for the cues that specify them.\n *\n * @default false\n * @platform android\n */\n applyEmbeddedStyles?: boolean;\n};\n\n/**\n * Configuration for controlling the visibility of player control buttons.\n *\n * > The fullscreen button should be controlled with [`fullscreenOptions.enable`](#fullscreenoptions).\n *\n * @platform android\n */\nexport type ButtonOptions = {\n /**\n * Whether to show the next button.\n * @default false\n */\n showNext?: boolean;\n /**\n * Whether to show the previous button.\n * @default false\n */\n showPrevious?: boolean;\n /**\n * Whether to show the seek forward button.\n * @default true\n */\n showSeekForward?: boolean;\n /**\n * Whether to show the seek backward button.\n * @default true\n */\n showSeekBackward?: boolean;\n /**\n * Whether to show the subtitles button.\n * - `true`: Button is always visible\n * - `false`: Button is never visible\n * - `undefined`: Button is visible only when subtitles are available (default behavior)\n * @default undefined\n */\n showSubtitles?: boolean | null;\n /**\n * Whether to show the settings button.\n * @default true\n */\n showSettings?: boolean;\n /**\n * Whether to show the play/pause button.\n * @default true\n */\n showPlayPause?: boolean;\n /**\n * Whether to show the bottom control bar (containing time, progress bar, and buttons).\n * When set to `false`, the entire bottom bar including the progress bar will be hidden.\n *\n * > **Note**: The bottom bar is always visible in fullscreen mode to allow users to exit fullscreen.\n *\n * @default true\n */\n showBottomBar?: boolean;\n};\n\n/**\n * Describes the orientation of the video in fullscreen mode. Available values are:\n * - `default`: The video is displayed in any of the available device rotations.\n * - `portrait`: The video is displayed in one of two available portrait orientations and rotates between them.\n * - `portraitUp`: The video is displayed in the portrait orientation - the notch of the phone points upwards.\n * - `portraitDown`: The video is displayed in the portrait orientation - the notch of the phone points downwards.\n * - `landscape`: The video is displayed in one of two available landscape orientations and rotates between them.\n * - `landscapeLeft`: The video is displayed in the left landscape orientation - the notch of the phone is in the left palm of the user.\n * - `landscapeRight`: The video is displayed in the right landscape orientation - the notch of the phone is in the right palm of the user.\n */\nexport type FullscreenOrientation =\n | 'default'\n | 'portrait'\n | 'portraitUp'\n | 'portraitDown'\n | 'landscape'\n | 'landscapeLeft'\n | 'landscapeRight';\n\n/**\n * Determines whether the player keeps fullscreen when Picture in Picture (PiP) stops.\n * Only has an effect if the player was in fullscreen when PiP started.\n * - `'always'`: Always re-enter fullscreen when PiP stops.\n * - `'autoEnter'`: Re-enter fullscreen only when PiP was started automatically by the app going to the background.\n * - `'never'`: Do not re-enter fullscreen when PiP stops.\n */\nexport type KeepFullscreenOnPiPStopBehavior = 'always' | 'autoEnter' | 'never';\n\n/**\n * Describes the options for fullscreen video mode.\n */\nexport type FullscreenOptions = {\n /**\n * Specifies whether the fullscreen mode should be available to the user. When `false`, the fullscreen button will be hidden in the player.\n * @default true\n */\n enable: boolean;\n /**\n * Specifies the orientation of the video in fullscreen mode.\n * @default 'default'\n * @platform android\n * @platform ios\n */\n orientation?: FullscreenOrientation;\n /**\n * Specifies whether the app should exit fullscreen mode when the device is rotated to a different orientation than the one specified in the `orientation` prop.\n * For example, if the `orientation` prop is set to `landscape` and the device is rotated to `portrait`, the app will exit fullscreen mode.\n *\n * > This prop will have no effect if the `orientation` prop is set to `default`.\n * > The `VideoView` will never auto-exit fullscreen when the device auto-rotate feature has been disabled in settings.\n *\n * @default false\n * @platform android\n * @platform ios\n */\n autoExitOnRotate?: boolean;\n\n /**\n * Determines whether the player keeps fullscreen when Picture in Picture (PiP) stops.\n * Only has an effect if the player was in fullscreen when PiP started.\n *\n * @default 'autoEnter'\n * @platform ios\n */\n keepFullscreenOnPiPStop?: KeepFullscreenOnPiPStopBehavior;\n};\n"]}
|
|
@@ -21,6 +21,15 @@ internal struct SubtitleSource: Record {
|
|
|
21
21
|
@Field
|
|
22
22
|
var `default`: Bool = false
|
|
23
23
|
|
|
24
|
+
/// How far above the bottom of the video this track renders, as a fraction of the video height.
|
|
25
|
+
///
|
|
26
|
+
/// Applied by rewriting the WebVTT with a `line:` cue setting, because it has to be baked in when the
|
|
27
|
+
/// composition is built — see `VideoPlayerSubtitleSideload`. It lives on the source rather than on
|
|
28
|
+
/// `SubtitleStyle` for that reason: the composition is built from a `VideoSource` alone, before any
|
|
29
|
+
/// `VideoView` has handed the player a style.
|
|
30
|
+
@Field
|
|
31
|
+
var bottomOffset: Double? = nil
|
|
32
|
+
|
|
24
33
|
/// Whether the source is a SubRip (`.srt`) file, which must be converted to WebVTT before sideloading.
|
|
25
34
|
var isSrt: Bool {
|
|
26
35
|
if let format {
|
|
@@ -107,8 +107,9 @@ internal enum VideoPlayerSubtitleSideload {
|
|
|
107
107
|
return nil
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
// Fast path: a local WebVTT file can be used as-is
|
|
111
|
-
|
|
110
|
+
// Fast path: a local WebVTT file can be used as-is — unless it has to be rewritten to carry a
|
|
111
|
+
// line position.
|
|
112
|
+
if uri.isFileURL && !subtitle.isSrt && subtitle.bottomOffset == nil {
|
|
112
113
|
return uri
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -123,14 +124,19 @@ internal enum VideoPlayerSubtitleSideload {
|
|
|
123
124
|
return nil
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
else {
|
|
127
|
+
// Decode only when there is something to rewrite, so an untouched WebVTT keeps its original bytes
|
|
128
|
+
// (and its original encoding) instead of being round-tripped through UTF-8.
|
|
129
|
+
if subtitle.isSrt || subtitle.bottomOffset != nil {
|
|
130
|
+
guard var text = String(data: data, encoding: .utf8) else {
|
|
131
131
|
return nil
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
if subtitle.isSrt {
|
|
134
|
+
text = SubtitleConverter.srtToVtt(text)
|
|
135
|
+
}
|
|
136
|
+
if let bottomOffset = subtitle.bottomOffset {
|
|
137
|
+
text = SubtitleConverter.applyLinePosition(text, bottomOffset: bottomOffset)
|
|
138
|
+
}
|
|
139
|
+
data = Data(text.utf8)
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
return writeTempVtt(data: data, source: subtitle)
|
|
@@ -160,7 +166,26 @@ internal enum VideoPlayerSubtitleSideload {
|
|
|
160
166
|
|
|
161
167
|
// Derive a process-stable filename (SHA-256) so identical subtitles always resolve to the same file.
|
|
162
168
|
// Swift's `Data.hashValue` is randomly seeded per launch, which would leak a new temp file every run.
|
|
163
|
-
|
|
169
|
+
//
|
|
170
|
+
// `bottomOffset` is part of the identity because it changes the bytes written: without it, the
|
|
171
|
+
// reuse check below would hand back a rendition positioned for a previous offset.
|
|
172
|
+
//
|
|
173
|
+
// `formatVersion` covers the same hazard across an *upgrade*: the inputs can be identical while
|
|
174
|
+
// the bytes this version writes are not, and the reuse check would then serve the file the
|
|
175
|
+
// previous version wrote — on exactly the devices being used to test the change. Bump it whenever
|
|
176
|
+
// the written form changes. v2: `applyLinePosition` gained the `,end` line alignment.
|
|
177
|
+
//
|
|
178
|
+
// Joined on NUL rather than a hyphen because a hyphen occurs in both of the variable parts — BCP-47
|
|
179
|
+
// tags carry one (`pt-BR`) and so does almost every URL — which lets two different subtitles
|
|
180
|
+
// flatten to the same string and collide on the hash. NUL cannot appear in either.
|
|
181
|
+
let formatVersion = "v2"
|
|
182
|
+
let offsetKey = source.bottomOffset.map { String(format: "%.4f", $0) } ?? ""
|
|
183
|
+
let identifier = [
|
|
184
|
+
formatVersion,
|
|
185
|
+
source.uri?.absoluteString ?? "",
|
|
186
|
+
source.language ?? "",
|
|
187
|
+
offsetKey
|
|
188
|
+
].joined(separator: "\u{0}")
|
|
164
189
|
let digest = SHA256.hash(data: Data(identifier.utf8))
|
|
165
190
|
let hashString = digest.compactMap { String(format: "%02x", $0) }.joined()
|
|
166
191
|
let fileURL = directory.appendingPathComponent("\(hashString).vtt")
|
|
@@ -183,6 +208,11 @@ internal enum VideoPlayerSubtitleSideload {
|
|
|
183
208
|
|
|
184
209
|
/// Minimal SubRip (`.srt`) → WebVTT (`.vtt`) converter. AVFoundation only reads WebVTT as a `.text` track.
|
|
185
210
|
internal enum SubtitleConverter {
|
|
211
|
+
/// A WebVTT cue timing line, anchored at the start: `[HH:]MM:SS.mmm --> [HH:]MM:SS.mmm`, optionally
|
|
212
|
+
/// followed by cue settings. Hours are optional per the WebVTT grammar.
|
|
213
|
+
private static let cueTimingPattern =
|
|
214
|
+
#"^[ \t]*(?:\d{2,}:)?[0-5]\d:[0-5]\d\.\d{3}[ \t]+-->[ \t]+(?:\d{2,}:)?[0-5]\d:[0-5]\d\.\d{3}"#
|
|
215
|
+
|
|
186
216
|
static func srtToVtt(_ srt: String) -> String {
|
|
187
217
|
let normalized = srt
|
|
188
218
|
.replacingOccurrences(of: "\r\n", with: "\n")
|
|
@@ -200,4 +230,52 @@ internal enum SubtitleConverter {
|
|
|
200
230
|
}
|
|
201
231
|
return "WEBVTT\n\n" + converted
|
|
202
232
|
}
|
|
233
|
+
|
|
234
|
+
/// Adds a WebVTT `line:` cue setting to every cue that does not already position itself.
|
|
235
|
+
///
|
|
236
|
+
/// This is how a subtitle is moved clear of burned-in captions on iOS. AVFoundation renders legible
|
|
237
|
+
/// tracks itself and `AVPlayerItem.textStyleRules` carries no positioning, so the position has to
|
|
238
|
+
/// travel inside the WebVTT — which is also why it is fixed when the composition is built.
|
|
239
|
+
///
|
|
240
|
+
/// - Parameter bottomOffset: fraction of the video height to sit above the bottom, matching the
|
|
241
|
+
/// Android `subtitleStyle.bottomOffset`. WebVTT counts `line` down from the top, so it is inverted.
|
|
242
|
+
///
|
|
243
|
+
/// The emitted setting is `line:<percentage>,end`. The trailing `,end` is the WebVTT *line alignment*
|
|
244
|
+
/// component — not a separate `line-align` setting, which is not one of the six cue setting names —
|
|
245
|
+
/// and it is what makes the percentage refer to the *bottom* edge of the cue box, matching Android's
|
|
246
|
+
/// `setBottomPaddingFraction`. A bare percentage anchors the *top*, so a cue wrapping onto two lines
|
|
247
|
+
/// hangs a full line lower on iOS than on Android; at typical caption sizes that is 5–13% of the video
|
|
248
|
+
/// height, comparable to the offset itself, and enough to put the second line back over the burned-in
|
|
249
|
+
/// captions this exists to escape. It also fixes `bottomOffset: 0`, which as a bare `line:100%` put the
|
|
250
|
+
/// top of the box at the bottom edge of the video and hid the subtitle entirely.
|
|
251
|
+
///
|
|
252
|
+
/// The alignment component is a known risk: a parser that predates it does not merely ignore it, it
|
|
253
|
+
/// discards the whole `line` setting, which would leave captions unpositioned rather than
|
|
254
|
+
/// top-anchored. There is no form that degrades gracefully — the two behaviours are expressed by the
|
|
255
|
+
/// same setting — so whether AVFoundation honours it has to be confirmed on a device. See FORK.md §5.
|
|
256
|
+
static func applyLinePosition(_ vtt: String, bottomOffset: Double) -> String {
|
|
257
|
+
let clamped = min(max(bottomOffset, 0), 1)
|
|
258
|
+
let linePercentage = Int(((1 - clamped) * 100).rounded())
|
|
259
|
+
|
|
260
|
+
let normalized = vtt
|
|
261
|
+
.replacingOccurrences(of: "\r\n", with: "\n")
|
|
262
|
+
.replacingOccurrences(of: "\r", with: "\n")
|
|
263
|
+
|
|
264
|
+
let positioned = normalized.components(separatedBy: "\n").map { line -> String in
|
|
265
|
+
// Only cue timing lines carry cue settings. Matched against the timestamp syntax rather than by
|
|
266
|
+
// looking for the arrow, because subtitle *text* is free to contain "-->" — dialogue and lyrics
|
|
267
|
+
// do — and appending a cue setting to it would print the setting on screen.
|
|
268
|
+
guard line.range(of: Self.cueTimingPattern, options: .regularExpression) != nil else {
|
|
269
|
+
return line
|
|
270
|
+
}
|
|
271
|
+
// A cue that already positions itself was written that way deliberately — left alone, matching
|
|
272
|
+
// how the Android side passes explicitly positioned cues through untouched.
|
|
273
|
+
guard !line.contains("line:") else {
|
|
274
|
+
return line
|
|
275
|
+
}
|
|
276
|
+
return "\(line) line:\(linePercentage)%,end"
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return positioned.joined(separator: "\n")
|
|
280
|
+
}
|
|
203
281
|
}
|
package/package.json
CHANGED
package/src/VideoPlayer.types.ts
CHANGED
|
@@ -459,6 +459,26 @@ export type SubtitleSource = {
|
|
|
459
459
|
* @default false
|
|
460
460
|
*/
|
|
461
461
|
default?: boolean;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* How far above the bottom of the video this track renders, as a fraction of the video height (0–1).
|
|
465
|
+
*
|
|
466
|
+
* Useful when a video already carries burned-in (hardcoded) subtitles and the sideloaded ones would
|
|
467
|
+
* otherwise be drawn on top of them.
|
|
468
|
+
*
|
|
469
|
+
* It sits here rather than on [`subtitleStyle`](#videoviewprops) because iOS can only apply it while
|
|
470
|
+
* the subtitle is being merged into the video, which happens from the `VideoSource` alone — before any
|
|
471
|
+
* `VideoView` has supplied a style. Changing it therefore takes effect the next time the source loads.
|
|
472
|
+
*
|
|
473
|
+
* > **Note:** cues that position themselves are left alone, so this does nothing for a WebVTT track
|
|
474
|
+
* > whose cues already carry a `line:` setting.
|
|
475
|
+
*
|
|
476
|
+
* On **Android** use [`subtitleStyle.bottomOffset`](#subtitlestylebottomoffset) instead, which does the
|
|
477
|
+
* same thing for every track at once and updates live.
|
|
478
|
+
*
|
|
479
|
+
* @platform ios
|
|
480
|
+
*/
|
|
481
|
+
bottomOffset?: number;
|
|
462
482
|
};
|
|
463
483
|
|
|
464
484
|
/**
|
package/src/VideoView.types.ts
CHANGED
|
@@ -288,7 +288,14 @@ export type SubtitleStyle = {
|
|
|
288
288
|
edgeColor?: string;
|
|
289
289
|
|
|
290
290
|
/**
|
|
291
|
-
* The distance of the subtitles from the bottom of the video, expressed as a fraction
|
|
291
|
+
* The distance of the subtitles from the bottom of the video, expressed as a fraction (`0`–`1`).
|
|
292
|
+
*
|
|
293
|
+
* The fraction is of the content frame, which with the default `contentFit="contain"` is the displayed
|
|
294
|
+
* picture rather than the whole view — so one value keeps captions at the same height relative to the
|
|
295
|
+
* image across screen sizes, pixel densities, orientations and letterboxed sources, with no conversion
|
|
296
|
+
* needed. With `contentFit="cover"` or `"fill"` the content frame is the view.
|
|
297
|
+
*
|
|
298
|
+
* @default 0.08
|
|
292
299
|
* @platform android
|
|
293
300
|
*/
|
|
294
301
|
bottomOffset?: number;
|