react-native-alpha-video 1.0.0 → 1.0.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/README.md CHANGED
@@ -20,7 +20,7 @@ One repository, four packages. The software decoders are [codec modules](#codecs
20
20
  |---|---|---|
21
21
  | `react-native-alpha-video` (npm) | React Native, and react-native-web | [React Native](#react-native) |
22
22
  | `dev.mediacache:alpha-video` (Maven Central), with `alpha-video-vpx` and `alpha-video-av1` | Android | [Android](#android) |
23
- | `alpha-video` (Swift Package Manager: products `AlphaVideoVPX`, `AlphaVideoAV1`; CocoaPods: `alpha-video`) | iOS | [iOS](#ios) |
23
+ | `alpha-video` (Swift Package Manager: products `AlphaVideoVPX`, `AlphaVideoAV1`, `AlphaVideoSource`; CocoaPods: `alpha-video`) | iOS | [iOS](#ios) |
24
24
  | `alpha-video-web` (npm) | Browsers: plain JS/TS, or React | [Web](#web), [web/README.md](web/README.md) |
25
25
 
26
26
  See it next to the other formats in the [demo app](#demo-and-related-packages).
@@ -171,9 +171,9 @@ The package works on both React Native architectures: the new one (a Fabric view
171
171
 
172
172
  ```gradle
173
173
  dependencies {
174
- implementation 'dev.mediacache:alpha-video:1.0.0'
175
- implementation 'dev.mediacache:alpha-video-vpx:1.0.0' // libvpx: VP8 and VP9 in software
176
- implementation 'dev.mediacache:alpha-video-av1:1.0.0' // dav1d: AV1 in software
174
+ implementation 'dev.mediacache:alpha-video:1.0.1'
175
+ implementation 'dev.mediacache:alpha-video-vpx:1.0.1' // libvpx: VP8 and VP9 in software
176
+ implementation 'dev.mediacache:alpha-video-av1:1.0.1' // dav1d: AV1 in software
177
177
  }
178
178
  ```
179
179
 
@@ -182,7 +182,7 @@ The core alone plays through MediaCodec; each codec module adds a software decod
182
182
  The core brings [range-source](https://github.com/MediaCacheDev/range-source) (`dev.mediacache:range-source`, about 320 KB per architecture), which reads URLs by range and keeps them between plays. To leave it out ([Without range-source](#without-range-source)):
183
183
 
184
184
  ```gradle
185
- implementation('dev.mediacache:alpha-video:1.0.0') {
185
+ implementation('dev.mediacache:alpha-video:1.0.1') {
186
186
  exclude group: 'dev.mediacache', module: 'range-source'
187
187
  }
188
188
  ```
@@ -230,18 +230,19 @@ pod 'alpha-video', '1.0.0'
230
230
  pod 'alpha-video', '1.0.0', :subspecs => ['VPX', 'Source']
231
231
  ```
232
232
 
233
- **Swift Package Manager**, package `alpha-video`, a product per codec, each with the core:
233
+ **Swift Package Manager**, package `alpha-video`, a product per codec, each with the core, and `AlphaVideoSource` for range-source:
234
234
 
235
235
  ```swift
236
- .package(url: "https://github.com/MediaCacheDev/alpha-video", from: "1.0.0")
237
- // in the target's dependencies, one or both:
236
+ .package(url: "https://github.com/MediaCacheDev/alpha-video", from: "1.0.1")
237
+ // in the target's dependencies, one codec or both, and range-source:
238
238
  .product(name: "AlphaVideoVPX", package: "alpha-video"),
239
239
  .product(name: "AlphaVideoAV1", package: "alpha-video"),
240
+ .product(name: "AlphaVideoSource", package: "alpha-video"),
240
241
  ```
241
242
 
242
- The module is `AlphaVideo` either way. libvpx and dav1d come prebuilt, each with its codec.
243
+ In an Xcode project, add the package (File > Add Package Dependencies) and pick the same products for the app target. The module is `AlphaVideo` either way. libvpx and dav1d come prebuilt, each with its codec.
243
244
 
244
- Both bring [range-source](https://github.com/MediaCacheDev/range-source), which reads URLs by range and keeps them between plays: the pod's `Source` subspec, and the package's default trait, `Source` (Swift 6.1, Xcode 16.3 or later). To leave it out ([Without range-source](#without-range-source)), name the pod's subspecs without it, `:subspecs => ['VPX', 'AV1']`. From a package, depend on the player with `traits: []`. An Xcode app project can't choose a package's traits, so an app that adds the package in Xcode always has range-source.
245
+ Both include [range-source](https://github.com/MediaCacheDev/range-source), which reads URLs by range and keeps them between plays, by default: the pod's `Source` subspec, and the package's `AlphaVideoSource` product. To leave it out ([Without range-source](#without-range-source)), name the pod's subspecs without it, `:subspecs => ['VPX', 'AV1']`, or don't add `AlphaVideoSource`. It's a product rather than a package trait because Xcode before 26.4 applies no package traits, default ones included.
245
246
 
246
247
  `AlphaVideoView` is a `UIView`:
247
248
 
@@ -317,7 +318,7 @@ The player automatically detects whether a file contains a single YUVA stream or
317
318
 
318
319
  The examples under [Installation](#installation) and [Usage](#usage) set every option, each to its default. `paused`, `playbackSpeed` and `verbose` change at once; the others are read when a source loads, so set them before the source.
319
320
 
320
- iOS has no `decoder`: it always decodes in software. The prefetch and queue settings apply to the C++ core's software decoding; MediaCodec on Android streams with its own buffers and ignores them, and so does the web, where the browser plays the file. On the web `mode`, `decoder`, `source.dataSource` and the stream and queue props have no effect (see [Web](#web)).
321
+ iOS has no `decoder`: it always decodes in software. The prefetch and queue settings apply to the C++ core's software decoding; MediaCodec on Android streams with its own buffers and ignores them, and so does the web, where the browser plays the file. On the web `mode`, `decoder` and the stream and queue props have no effect (see [Web](#web)).
321
322
 
322
323
  ### Props
323
324
 
@@ -532,7 +533,11 @@ final class MyDataSource: NSObject, RangeDataSource {
532
533
  RangeDataSources.register(MyDataSource(), name: "mine")
533
534
  ```
534
535
 
535
- **React Native:** register in native code, as above, in `MainApplication` (Android) and `AppDelegate` (iOS, `import RangeSource`).
536
+ **React Native:** register in native code, as above, in `MainApplication` (Android) and `AppDelegate` (iOS, `import RangeSource`). On react-native-web, register a function that makes a byte source for a URI with range-source-web (1.2.0 or later): `RangeDataSources.register('mine', (uri, { headers }) => myByteSource(uri, headers))`.
537
+
538
+ **MediaCache** is a data source on Android, iOS, React Native (react-native-web included) and the web. `cache.registerRangeDataSource()` registers it as `'mediacache'`. The player's files then stream through the cache: each byte is fetched once, shared with every other player of the file, and kept under the cache's eviction, TTL and offline replay. See [Transparent Video](https://github.com/MediaCacheDev/media-cache#transparent-video-alpha-video-and-alpha-video-gpu) in MediaCache's README.
539
+
540
+ MediaCache needs range-source on Android, iOS and React Native: MediaCache fetches and stores the bytes, and range-source passes the player's reads to it. range-source is included by default ([Installation](#installation)), but an app that left it out has to add it back, and `registerRangeDataSource` returns false without it. On the web, the player's range-source-web is always included.
536
541
 
537
542
  ### Web
538
543
 
@@ -544,9 +549,9 @@ RangeDataSources.register(MyDataSource(), name: "mine")
544
549
 
545
550
  Only a file that no engine can decode ends in an error (`NO_ALPHA_PATH`). Each WebAssembly module is loaded only when its engine runs, and nothing needs hosting: the modules and Workers are inside the package. Every test card passes through every engine that can decode it, in Playwright's Chromium, Firefox and WebKit; real Safari and phones are still to be tested. The API, the React component and speed figures are in [web/README.md](web/README.md).
546
551
 
547
- **On react-native-web, `AlphaVideoView` plays through `alpha-video-web`**, which is installed with this package. iOS and Android bundles don't include it: the component imports it from a `.web.tsx` file. It honours `source` (with `source.headers`, sent with every request; on the web `source` can also be a `Blob` or `File`, or a byte source, `{ size, read(offset, length, signal) }`, kept as the same object between renders), `cacheKey` (naming the stored copy), `storage` (`'disk'` is the browser's storage, as `'auto'`), `maxStorageBytes`, `autoPlay`, `loop`, `paused`, `playbackSpeed`, `webDecoder`, `onLoad`, `onError`, `onEnd` and the ref methods. `mode`, `decoder`, `source.dataSource` and the stream and queue props have no effect. `asset://path` is served from the site root.
552
+ **On react-native-web, `AlphaVideoView` plays through `alpha-video-web`**, which is installed with this package. iOS and Android bundles don't include it: the component imports it from a `.web.tsx` file. It honours `source` (with `source.headers`, sent with every request; on the web `source` can also be a `Blob` or `File`, or a byte source, `{ size, read(offset, length, signal) }`, kept as the same object between renders), `cacheKey` (naming the stored copy), `storage` (`'disk'` is the browser's storage, as `'auto'`), `maxStorageBytes`, `autoPlay`, `loop`, `paused`, `playbackSpeed`, `webDecoder`, `onLoad`, `onError`, `onEnd` and the ref methods, and `source.dataSource`, a name registered with range-source-web ([Data sources](#data-sources)). `mode`, `decoder` and the stream and queue props have no effect. `asset://path` is served from the site root.
548
553
 
549
- - **Bundling:** the WebAssembly engines are separate chunks, loaded by `import()` only when a file needs them. With webpack, Vite or esbuild that needs nothing extra.
554
+ - **Bundling:** the demuxer and the WebAssembly engines are separate chunks, loaded by `import()` only when a file needs them. With webpack, Vite or esbuild that needs nothing extra. Metro loads them through `@expo/metro-runtime`: Expo Router apps have it, and other Expo apps add `import '@expo/metro-runtime';` at the top of their entry file.
550
555
  - **Storage for `<video>`:** copy `range-source-web/dist/range-source-sw.js` to the site's root and call `registerRangeSourceWorker('/range-source-sw.js')` from `alpha-video-web` before `AppRegistry.runApplication` ([details](web/README.md#video-and-the-service-worker)).
551
556
  - **Content-Security-Policy:** the software engine's Workers start from `blob:` URLs, so a page with a CSP must allow `worker-src blob:`.
552
557
  - **Threads:** a cross-origin isolated page (COOP and COEP headers) lets the software decoders run threads ([details](web/README.md#threads)).
@@ -836,7 +841,7 @@ The fixtures in `tests/fixtures/` are test cards from the tester app: every fram
836
841
 
837
842
  The host tests need CMake, Ninja, pkg-config, libvpx and dav1d (`brew install cmake ninja pkg-config libvpx dav1d`; add `ffmpeg` for `demuxer_compare`), and a [range-source](https://github.com/MediaCacheDev/range-source) checkout beside this repository (or `-DRANGE_SOURCE_DIR=`), whose reader they link as an app does. The web tests need `npm install` and `npm run build` in `web/` first, and Playwright's browsers (`npx playwright install chromium firefox webkit`); `test:streaming` makes its clip with ffmpeg.
838
843
 
839
- The iOS tests are Swift Package test targets: `VPXOnlyTests` and `AV1OnlyTests` each link one codec module alone, so they run one at a time, and `SourceTests` links range-source. `tests/ios/WithoutRangeSource` is a package that depends on this one with `traits: []`, as an app leaving range-source out does. They need the codec xcframeworks: with `prebuilt/` in place (see [Codec Build](#codec-build)), `scripts/build_xcframework.sh` makes them there, where `Package.swift` looks first. `RANGE_SOURCE_PATH` points them at a range-source checkout instead of its release.
844
+ The iOS tests are Swift Package test targets: `VPXOnlyTests` and `AV1OnlyTests` each link one codec module alone, so they run one at a time, and `SourceTests` links range-source. `tests/ios/WithoutRangeSource` is a package that takes `AlphaVideoVPX` without `AlphaVideoSource`, as an app leaving range-source out does. They need the codec xcframeworks: with `prebuilt/` in place (see [Codec Build](#codec-build)), `scripts/build_xcframework.sh` makes them there, where `Package.swift` looks first. `RANGE_SOURCE_PATH` points them at a range-source checkout instead of its release.
840
845
 
841
846
  ```bash
842
847
  for t in VPXOnlyTests AV1OnlyTests SourceTests; do
@@ -21,7 +21,7 @@ def safeExtGet(name, fallback) {
21
21
  }
22
22
 
23
23
  group = 'dev.mediacache'
24
- version = '1.0.0'
24
+ version = '1.0.1'
25
25
 
26
26
  android {
27
27
  namespace 'dev.mediacache.alphavideo'
@@ -31,6 +31,7 @@ android {
31
31
  defaultConfig {
32
32
  minSdk safeExtGet('minSdkVersion', 24)
33
33
  consumerProguardFiles 'consumer-rules.pro'
34
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
34
35
 
35
36
  externalNativeBuild {
36
37
  cmake {
@@ -78,6 +79,10 @@ dependencies {
78
79
  // Included by default and removable: an app can exclude it, and the player then falls back
79
80
  // to the basic input (RangeSourceLink)
80
81
  api 'dev.mediacache:range-source:1.0.0'
82
+
83
+ androidTestImplementation 'androidx.test:core:1.6.1'
84
+ androidTestImplementation 'androidx.test:runner:1.6.2'
85
+ androidTestImplementation 'androidx.test.ext:junit:1.2.1'
81
86
  }
82
87
 
83
88
  if (project == rootProject) {
@@ -27,6 +27,7 @@ import java.util.Map;
27
27
  import java.util.concurrent.ExecutorService;
28
28
  import java.util.concurrent.Executors;
29
29
  import java.util.concurrent.Future;
30
+ import java.util.concurrent.RejectedExecutionException;
30
31
  import java.util.concurrent.atomic.AtomicBoolean;
31
32
 
32
33
  /**
@@ -1017,7 +1018,7 @@ public class AlphaVideoView extends TextureView implements
1017
1018
  this.surfaceHeight = height;
1018
1019
 
1019
1020
  // Update viewport on render thread
1020
- renderThread.execute(() -> {
1021
+ onRenderThread(() -> {
1021
1022
  if (eglSurface != null && eglContext != null) {
1022
1023
  makeCurrent();
1023
1024
  GLES20.glViewport(0, 0, width, height);
@@ -1036,14 +1037,9 @@ public class AlphaVideoView extends TextureView implements
1036
1037
  this.surfaceTexture = null;
1037
1038
 
1038
1039
  // Clean up EGL on render thread WITHOUT blocking main thread
1039
- // This prevents ANRs when multiple players are destroyed simultaneously
1040
- renderThread.execute(() -> {
1041
- try {
1042
- destroyEGL();
1043
- } catch (Exception e) {
1044
- Log.e(TAG, "Error during EGL cleanup: " + e.getMessage());
1045
- }
1046
- });
1040
+ // This prevents ANRs when multiple players are destroyed simultaneously.
1041
+ // After release() there is no render thread, and release() has cleaned up already.
1042
+ onRenderThread(this::destroyEGLSafely);
1047
1043
 
1048
1044
  // Return false - we'll manage SurfaceTexture lifecycle ourselves to avoid blocking
1049
1045
  // The render thread will clean up EGL and we don't need the SurfaceTexture after that
@@ -1355,8 +1351,28 @@ public class AlphaVideoView extends TextureView implements
1355
1351
 
1356
1352
  private void requestRender() {
1357
1353
  // Prevent queuing multiple render calls
1358
- if (renderPending.compareAndSet(false, true)) {
1359
- renderThread.execute(this::doRender);
1354
+ if (renderPending.compareAndSet(false, true) && !onRenderThread(this::doRender)) {
1355
+ renderPending.set(false);
1356
+ }
1357
+ }
1358
+
1359
+ /** Runs a task on the render thread. False when there is none, after {@link #release()}. */
1360
+ private boolean onRenderThread(Runnable task) {
1361
+ ExecutorService thread = renderThread;
1362
+ if (thread == null || thread.isShutdown()) return false;
1363
+ try {
1364
+ thread.execute(task);
1365
+ return true;
1366
+ } catch (RejectedExecutionException e) {
1367
+ return false;
1368
+ }
1369
+ }
1370
+
1371
+ private void destroyEGLSafely() {
1372
+ try {
1373
+ destroyEGL();
1374
+ } catch (Exception e) {
1375
+ Log.e(TAG, "Error during EGL cleanup: " + e.getMessage());
1360
1376
  }
1361
1377
  }
1362
1378
 
@@ -1778,8 +1794,8 @@ public class AlphaVideoView extends TextureView implements
1778
1794
  }
1779
1795
  }
1780
1796
 
1781
- // Shutdown render thread
1782
- if (renderThread != null && !renderThread.isShutdown()) {
1797
+ // Shutdown render thread, freeing EGL on it first: the surface going away later can't
1798
+ if (onRenderThread(this::destroyEGLSafely)) {
1783
1799
  renderThread.shutdown();
1784
1800
  }
1785
1801
  }
@@ -48,7 +48,7 @@ static const int64_t AlphaVideoDefaultMaxStorageBytes = 512LL * 1024 * 1024;
48
48
  static void AlphaVideoWarnWithoutAddOn(void) {
49
49
  static dispatch_once_t once;
50
50
  dispatch_once(&once, ^{
51
- NSLog(@"[AlphaVideo] range-source isn't installed (the RangeSource SwiftPM product, the alpha-video pod's "
51
+ NSLog(@"[AlphaVideo] range-source isn't installed (the AlphaVideoSource SwiftPM product, the alpha-video pod's "
52
52
  @"Source subspec, or for React Native npm i react-native-range-source): URLs are downloaded whole for "
53
53
  @"each play, without range requests, storage, maxStorageBytes or cacheKey");
54
54
  });
@@ -240,7 +240,7 @@ static void AlphaVideoWarnWithoutAddOn(void) {
240
240
  if (!AlphaVideoRangeSourcePlugin()) {
241
241
  if (appSource) {
242
242
  [self playerDidEncounterError:[NSString stringWithFormat:
243
- @"The data source \"%@\" needs the range-source add-on (the RangeSource SwiftPM product, the "
243
+ @"The data source \"%@\" needs the range-source add-on (the AlphaVideoSource SwiftPM product, the "
244
244
  @"alpha-video pod's Source subspec, or for React Native react-native-range-source), which the "
245
245
  @"app doesn't include", self.dataSource]
246
246
  code:AlphaVideoErrorNoSourceModule];
@@ -20,7 +20,7 @@ extern NSString *const AlphaVideoErrorNoDecoder;
20
20
 
21
21
  /**
22
22
  * The error code for a data source without the range-source add-on, which reads them (the
23
- * RangeSource SwiftPM product, the alpha-video pod's Source subspec, or for React Native
23
+ * AlphaVideoSource SwiftPM product, the alpha-video pod's Source subspec, or for React Native
24
24
  * react-native-range-source).
25
25
  */
26
26
  extern NSString *const AlphaVideoErrorNoSourceModule;
@@ -19,6 +19,11 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
19
19
  /** asset://path names the app's bundled media; on the web it is served from the site root. */function toWebUrl(uri) {
20
20
  return uri.startsWith('asset://') ? '/' + uri.slice('asset://'.length).replace(/^\/+/, '') : uri;
21
21
  }
22
+ /** range-source-web's `RangeDataSources`, read from its global symbol so this package needn't depend on it. */
23
+ function registeredDataSource(name) {
24
+ const scope = globalThis;
25
+ return scope[Symbol.for('range-source.dataSources')]?.get(name);
26
+ }
22
27
  const WebPlayer = exports.WebPlayer = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
23
28
  const {
24
29
  source,
@@ -40,9 +45,25 @@ const WebPlayer = exports.WebPlayer = /*#__PURE__*/(0, _react.forwardRef)((props
40
45
  onEnd
41
46
  } = props;
42
47
  // Streamed by range from http(s) URLs and kept in the browser's storage (OPFS, else IndexedDB)
43
- // by the canvas engines; <video> uses the HTTP cache. A Blob or byte source plays as it is.
48
+ // by the canvas engines; <video> uses the HTTP cache. A Blob or byte source plays as it is, and
49
+ // a `dataSource` name plays the byte source registered under it, which keeps the bytes itself.
44
50
  const uriSource = (0, _AlphaVideoView.isUriSource)(source) ? source : undefined;
45
- const src = (0, _AlphaVideoView.isUriSource)(source) ? source.uri ? toWebUrl(source.uri) : undefined : source;
51
+ const dataSource = uriSource?.dataSource;
52
+ const headersKey = JSON.stringify(uriSource?.headers ?? null);
53
+ const fromDataSource = (0, _react.useMemo)(() => dataSource && uriSource?.uri ? registeredDataSource(dataSource)?.(uriSource.uri, {
54
+ headers: uriSource.headers
55
+ }) ?? null : null,
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ [dataSource, uriSource?.uri, headersKey]);
58
+ const src = dataSource ? fromDataSource : (0, _AlphaVideoView.isUriSource)(source) ? source.uri ? toWebUrl(source.uri) : undefined : source;
59
+ (0, _react.useEffect)(() => {
60
+ if (dataSource && uriSource?.uri && !fromDataSource) {
61
+ onError?.({
62
+ error: `No data source is registered on the web under "${dataSource}"`
63
+ });
64
+ }
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [fromDataSource, dataSource, uriSource?.uri]);
46
67
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
47
68
  style: [styles.container, style],
48
69
  children: src ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.AlphaVideo, {
@@ -53,8 +74,10 @@ const WebPlayer = exports.WebPlayer = /*#__PURE__*/(0, _react.forwardRef)((props
53
74
  paused: paused,
54
75
  playbackSpeed: playbackSpeed,
55
76
  decoder: webDecoder,
56
- headers: uriSource?.headers,
57
- cacheKey: uriSource?.cacheKey || cacheKey,
77
+ headers: dataSource ? undefined : uriSource?.headers,
78
+ cacheKey:
79
+ // As on Android and iOS, a data source's bytes are stored only when storage is set
80
+ dataSource && storage !== 'disk' && storage !== 'memory' ? undefined : uriSource?.cacheKey || cacheKey || (dataSource ? uriSource?.uri : undefined),
58
81
  storage: storage === 'disk' ? 'auto' : storage,
59
82
  maxStorageBytes: maxStorageBytes,
60
83
  style: fill,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_react2","_AlphaVideoView","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","toWebUrl","uri","startsWith","slice","length","replace","WebPlayer","exports","forwardRef","props","ref","source","autoPlay","loop","paused","playbackSpeed","webDecoder","cacheKey","storage","maxStorageBytes","style","onLoad","onError","onEnd","uriSource","isUriSource","undefined","src","jsx","View","styles","container","children","AlphaVideo","decoder","headers","fill","totalFrames","width","height","error","message","code","displayName","StyleSheet","create","overflow"],"sourceRoot":"../../react-native/src","sources":["WebPlayer.web.tsx"],"mappings":";;;;;;AAMA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,eAAA,GAAAH,OAAA;AAAiG,IAAAI,WAAA,GAAAJ,OAAA;AAAA,SAAAD,wBAAAM,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAR,uBAAA,YAAAA,CAAAM,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AATjG;AACA;AACA;AACA;AACA;;AAOA,8FACA,SAASkB,QAAQA,CAACC,GAAW,EAAU;EACrC,OAAOA,GAAG,CAACC,UAAU,CAAC,UAAU,CAAC,GAAG,GAAG,GAAGD,GAAG,CAACE,KAAK,CAAC,UAAU,CAACC,MAAM,CAAC,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAGJ,GAAG;AAClG;AAEO,MAAMK,SAAS,GAAAC,OAAA,CAAAD,SAAA,gBAAG,IAAAE,iBAAU,EAAyC,CAACC,KAAK,EAAEC,GAAG,KAAK;EAC1F,MAAM;IAAEC,MAAM;IAAEC,QAAQ,GAAG,IAAI;IAAEC,IAAI,GAAG,IAAI;IAAEC,MAAM,GAAG,KAAK;IAAEC,aAAa,GAAG,CAAC;IAAEC,UAAU,GAAG,MAAM;IAAEC;EAAS,CAAC,GAAGR,KAAK;EACxH,MAAM;IAAES,OAAO;IAAEC;EAAgB,CAAC,GAAGV,KAAK;EAC1C,MAAM;IAAEW,KAAK;IAAEC,MAAM;IAAEC,OAAO;IAAEC;EAAM,CAAC,GAAGd,KAAK;EAC/C;EACA;EACA,MAAMe,SAAS,GAAG,IAAAC,2BAAW,EAACd,MAAM,CAAC,GAAGA,MAAM,GAAGe,SAAS;EAC1D,MAAMC,GAAG,GAAG,IAAAF,2BAAW,EAACd,MAAM,CAAC,GAAIA,MAAM,CAACV,GAAG,GAAGD,QAAQ,CAACW,MAAM,CAACV,GAAG,CAAC,GAAGyB,SAAS,GAAIf,MAAM;EAE1F,oBACE,IAAA/B,WAAA,CAAAgD,GAAA,EAACnD,YAAA,CAAAoD,IAAI;IAACT,KAAK,EAAE,CAACU,MAAM,CAACC,SAAS,EAAEX,KAAK,CAAE;IAAAY,QAAA,EACpCL,GAAG,gBACF,IAAA/C,WAAA,CAAAgD,GAAA,EAAClD,OAAA,CAAAuD,UAAU;MACTvB,GAAG,EAAEA,GAAI;MACTiB,GAAG,EAAEA,GAAI;MACTf,QAAQ,EAAEA,QAAS;MACnBC,IAAI,EAAEA,IAAK;MACXC,MAAM,EAAEA,MAAO;MACfC,aAAa,EAAEA,aAAc;MAC7BmB,OAAO,EAAElB,UAAW;MACpBmB,OAAO,EAAEX,SAAS,EAAEW,OAAQ;MAC5BlB,QAAQ,EAAEO,SAAS,EAAEP,QAAQ,IAAIA,QAAS;MAC1CC,OAAO,EAAEA,OAAO,KAAK,MAAM,GAAG,MAAM,GAAGA,OAAQ;MAC/CC,eAAe,EAAEA,eAAgB;MACjCC,KAAK,EAAEgB,IAAK;MACZf,MAAM,EAAEA,CAAC;QAAEgB,WAAW;QAAEC,KAAK;QAAEC,MAAM;QAAEL;MAAQ,CAAC,KAAKb,MAAM,GAAG;QAAEgB,WAAW;QAAEC,KAAK;QAAEC,MAAM;QAAEL;MAAQ,CAAC,CAAE;MACvGZ,OAAO,EAAGkB,KAAK,IAAKlB,OAAO,GAAG;QAAEkB,KAAK,EAAEA,KAAK,CAACC,OAAO;QAAEC,IAAI,EAAEF,KAAK,CAACE;MAAK,CAAC,CAAE;MAC1EnB,KAAK,EAAEA,CAAA,KAAMA,KAAK,GAAG,CAAC,CAAC;IAAE,CAC1B,CAAC,GACA;EAAI,CACJ,CAAC;AAEX,CAAC,CAAC;AAEFjB,SAAS,CAACqC,WAAW,GAAG,WAAW;AAEnC,MAAMb,MAAM,GAAGc,uBAAU,CAACC,MAAM,CAAC;EAC/Bd,SAAS,EAAE;IACTe,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;;AAEF;AACA,MAAMV,IAAyB,GAAG;EAAEE,KAAK,EAAE,MAAM;EAAEC,MAAM,EAAE;AAAO,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_react2","_AlphaVideoView","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","toWebUrl","uri","startsWith","slice","length","replace","registeredDataSource","name","scope","globalThis","Symbol","for","WebPlayer","exports","forwardRef","props","ref","source","autoPlay","loop","paused","playbackSpeed","webDecoder","cacheKey","storage","maxStorageBytes","style","onLoad","onError","onEnd","uriSource","isUriSource","undefined","dataSource","headersKey","JSON","stringify","headers","fromDataSource","useMemo","src","useEffect","error","jsx","View","styles","container","children","AlphaVideo","decoder","fill","totalFrames","width","height","message","code","displayName","StyleSheet","create","overflow"],"sourceRoot":"../../react-native/src","sources":["WebPlayer.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -19,6 +19,11 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
19
19
  /** asset://path names the app's bundled media; on the web it is served from the site root. */function toWebUrl(uri) {
20
20
  return uri.startsWith('asset://') ? '/' + uri.slice('asset://'.length).replace(/^\/+/, '') : uri;
21
21
  }
22
+ /** range-source-web's `RangeDataSources`, read from its global symbol so this package needn't depend on it. */
23
+ function registeredDataSource(name) {
24
+ const scope = globalThis;
25
+ return scope[Symbol.for('range-source.dataSources')]?.get(name);
26
+ }
22
27
  const WebPlayer = exports.WebPlayer = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
23
28
  const {
24
29
  source,
@@ -40,9 +45,25 @@ const WebPlayer = exports.WebPlayer = /*#__PURE__*/(0, _react.forwardRef)((props
40
45
  onEnd
41
46
  } = props;
42
47
  // Streamed by range from http(s) URLs and kept in the browser's storage (OPFS, else IndexedDB)
43
- // by the canvas engines; <video> uses the HTTP cache. A Blob or byte source plays as it is.
48
+ // by the canvas engines; <video> uses the HTTP cache. A Blob or byte source plays as it is, and
49
+ // a `dataSource` name plays the byte source registered under it, which keeps the bytes itself.
44
50
  const uriSource = (0, _AlphaVideoView.isUriSource)(source) ? source : undefined;
45
- const src = (0, _AlphaVideoView.isUriSource)(source) ? source.uri ? toWebUrl(source.uri) : undefined : source;
51
+ const dataSource = uriSource?.dataSource;
52
+ const headersKey = JSON.stringify(uriSource?.headers ?? null);
53
+ const fromDataSource = (0, _react.useMemo)(() => dataSource && uriSource?.uri ? registeredDataSource(dataSource)?.(uriSource.uri, {
54
+ headers: uriSource.headers
55
+ }) ?? null : null,
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ [dataSource, uriSource?.uri, headersKey]);
58
+ const src = dataSource ? fromDataSource : (0, _AlphaVideoView.isUriSource)(source) ? source.uri ? toWebUrl(source.uri) : undefined : source;
59
+ (0, _react.useEffect)(() => {
60
+ if (dataSource && uriSource?.uri && !fromDataSource) {
61
+ onError?.({
62
+ error: `No data source is registered on the web under "${dataSource}"`
63
+ });
64
+ }
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [fromDataSource, dataSource, uriSource?.uri]);
46
67
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
47
68
  style: [styles.container, style],
48
69
  children: src ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.AlphaVideo, {
@@ -53,8 +74,10 @@ const WebPlayer = exports.WebPlayer = /*#__PURE__*/(0, _react.forwardRef)((props
53
74
  paused: paused,
54
75
  playbackSpeed: playbackSpeed,
55
76
  decoder: webDecoder,
56
- headers: uriSource?.headers,
57
- cacheKey: uriSource?.cacheKey || cacheKey,
77
+ headers: dataSource ? undefined : uriSource?.headers,
78
+ cacheKey:
79
+ // As on Android and iOS, a data source's bytes are stored only when storage is set
80
+ dataSource && storage !== 'disk' && storage !== 'memory' ? undefined : uriSource?.cacheKey || cacheKey || (dataSource ? uriSource?.uri : undefined),
58
81
  storage: storage === 'disk' ? 'auto' : storage,
59
82
  maxStorageBytes: maxStorageBytes,
60
83
  style: fill,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_react2","_AlphaVideoView","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","toWebUrl","uri","startsWith","slice","length","replace","WebPlayer","exports","forwardRef","props","ref","source","autoPlay","loop","paused","playbackSpeed","webDecoder","cacheKey","storage","maxStorageBytes","style","onLoad","onError","onEnd","uriSource","isUriSource","undefined","src","jsx","View","styles","container","children","AlphaVideo","decoder","headers","fill","totalFrames","width","height","error","message","code","displayName","StyleSheet","create","overflow"],"sourceRoot":"../../react-native/src","sources":["WebPlayer.web.tsx"],"mappings":";;;;;;AAMA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,eAAA,GAAAH,OAAA;AAAiG,IAAAI,WAAA,GAAAJ,OAAA;AAAA,SAAAD,wBAAAM,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAR,uBAAA,YAAAA,CAAAM,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AATjG;AACA;AACA;AACA;AACA;;AAOA,8FACA,SAASkB,QAAQA,CAACC,GAAW,EAAU;EACrC,OAAOA,GAAG,CAACC,UAAU,CAAC,UAAU,CAAC,GAAG,GAAG,GAAGD,GAAG,CAACE,KAAK,CAAC,UAAU,CAACC,MAAM,CAAC,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAGJ,GAAG;AAClG;AAEO,MAAMK,SAAS,GAAAC,OAAA,CAAAD,SAAA,gBAAG,IAAAE,iBAAU,EAAyC,CAACC,KAAK,EAAEC,GAAG,KAAK;EAC1F,MAAM;IAAEC,MAAM;IAAEC,QAAQ,GAAG,IAAI;IAAEC,IAAI,GAAG,IAAI;IAAEC,MAAM,GAAG,KAAK;IAAEC,aAAa,GAAG,CAAC;IAAEC,UAAU,GAAG,MAAM;IAAEC;EAAS,CAAC,GAAGR,KAAK;EACxH,MAAM;IAAES,OAAO;IAAEC;EAAgB,CAAC,GAAGV,KAAK;EAC1C,MAAM;IAAEW,KAAK;IAAEC,MAAM;IAAEC,OAAO;IAAEC;EAAM,CAAC,GAAGd,KAAK;EAC/C;EACA;EACA,MAAMe,SAAS,GAAG,IAAAC,2BAAW,EAACd,MAAM,CAAC,GAAGA,MAAM,GAAGe,SAAS;EAC1D,MAAMC,GAAG,GAAG,IAAAF,2BAAW,EAACd,MAAM,CAAC,GAAIA,MAAM,CAACV,GAAG,GAAGD,QAAQ,CAACW,MAAM,CAACV,GAAG,CAAC,GAAGyB,SAAS,GAAIf,MAAM;EAE1F,oBACE,IAAA/B,WAAA,CAAAgD,GAAA,EAACnD,YAAA,CAAAoD,IAAI;IAACT,KAAK,EAAE,CAACU,MAAM,CAACC,SAAS,EAAEX,KAAK,CAAE;IAAAY,QAAA,EACpCL,GAAG,gBACF,IAAA/C,WAAA,CAAAgD,GAAA,EAAClD,OAAA,CAAAuD,UAAU;MACTvB,GAAG,EAAEA,GAAI;MACTiB,GAAG,EAAEA,GAAI;MACTf,QAAQ,EAAEA,QAAS;MACnBC,IAAI,EAAEA,IAAK;MACXC,MAAM,EAAEA,MAAO;MACfC,aAAa,EAAEA,aAAc;MAC7BmB,OAAO,EAAElB,UAAW;MACpBmB,OAAO,EAAEX,SAAS,EAAEW,OAAQ;MAC5BlB,QAAQ,EAAEO,SAAS,EAAEP,QAAQ,IAAIA,QAAS;MAC1CC,OAAO,EAAEA,OAAO,KAAK,MAAM,GAAG,MAAM,GAAGA,OAAQ;MAC/CC,eAAe,EAAEA,eAAgB;MACjCC,KAAK,EAAEgB,IAAK;MACZf,MAAM,EAAEA,CAAC;QAAEgB,WAAW;QAAEC,KAAK;QAAEC,MAAM;QAAEL;MAAQ,CAAC,KAAKb,MAAM,GAAG;QAAEgB,WAAW;QAAEC,KAAK;QAAEC,MAAM;QAAEL;MAAQ,CAAC,CAAE;MACvGZ,OAAO,EAAGkB,KAAK,IAAKlB,OAAO,GAAG;QAAEkB,KAAK,EAAEA,KAAK,CAACC,OAAO;QAAEC,IAAI,EAAEF,KAAK,CAACE;MAAK,CAAC,CAAE;MAC1EnB,KAAK,EAAEA,CAAA,KAAMA,KAAK,GAAG,CAAC,CAAC;IAAE,CAC1B,CAAC,GACA;EAAI,CACJ,CAAC;AAEX,CAAC,CAAC;AAEFjB,SAAS,CAACqC,WAAW,GAAG,WAAW;AAEnC,MAAMb,MAAM,GAAGc,uBAAU,CAACC,MAAM,CAAC;EAC/Bd,SAAS,EAAE;IACTe,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;;AAEF;AACA,MAAMV,IAAyB,GAAG;EAAEE,KAAK,EAAE,MAAM;EAAEC,MAAM,EAAE;AAAO,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_react2","_AlphaVideoView","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","toWebUrl","uri","startsWith","slice","length","replace","registeredDataSource","name","scope","globalThis","Symbol","for","WebPlayer","exports","forwardRef","props","ref","source","autoPlay","loop","paused","playbackSpeed","webDecoder","cacheKey","storage","maxStorageBytes","style","onLoad","onError","onEnd","uriSource","isUriSource","undefined","dataSource","headersKey","JSON","stringify","headers","fromDataSource","useMemo","src","useEffect","error","jsx","View","styles","container","children","AlphaVideo","decoder","fill","totalFrames","width","height","message","code","displayName","StyleSheet","create","overflow"],"sourceRoot":"../../react-native/src","sources":["WebPlayer.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -31,9 +31,9 @@ export interface AlphaVideoSource {
31
31
  /** Sent with every request for the URL, e.g. Authorization; given to a `dataSource` too */
32
32
  headers?: Record<string, string>;
33
33
  /**
34
- * iOS and Android: read the file through the data source the app registered under this name
35
- * (`AlphaVideoDataSources.register` on Android, `AlphaVideoDataSources.register(_:name:)`
36
- * on iOS) instead of the built-in fetcher.
34
+ * Read the file through the data source registered under this name instead of the built-in
35
+ * fetcher: range-source's `RangeDataSources` on Android and iOS, and range-source-web's on the
36
+ * web. MediaCache registers as `'mediacache'` with `cache.registerRangeDataSource()`.
37
37
  */
38
38
  dataSource?: string;
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"WebPlayer.web.d.ts","sourceRoot":"","sources":["../../../../react-native/src/WebPlayer.web.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAqB,MAAM,OAAO,CAAC;AAG1C,OAAO,EAAe,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAOjG,eAAO,MAAM,SAAS,+FAgCpB,CAAC"}
1
+ {"version":3,"file":"WebPlayer.web.d.ts","sourceRoot":"","sources":["../../../../react-native/src/WebPlayer.web.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,EAAe,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAejG,eAAO,MAAM,SAAS,+FAkDpB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-alpha-video",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Transparent video for React Native: VP8, VP9 or AV1 WebM with alpha on iOS and Android, decoded by a C++ core (libvpx, dav1d for AV1) or the Android hardware decoder, drawn with Metal and OpenGL ES. On the web, alpha-video-web shows the alpha in every browser: <video>, WebCodecs or WebAssembly decoding.",
5
5
  "source": "react-native/src/index.tsx",
6
6
  "main": "lib/commonjs/index.js",
@@ -31,6 +31,8 @@
31
31
  "!android/*/build",
32
32
  "!android/*/.cxx",
33
33
  "!android/local.properties",
34
+ "!android/src/androidTest",
35
+ "!ios/Sources/AlphaVideoSource",
34
36
  "!react-native/android/build",
35
37
  "!react-native/android/.gradle",
36
38
  "!react-native/android/.cxx",
@@ -114,7 +116,7 @@
114
116
  "typescript": "^5.0.0"
115
117
  },
116
118
  "dependencies": {
117
- "alpha-video-web": "^1.0.0"
119
+ "alpha-video-web": "^1.0.1"
118
120
  },
119
121
  "peerDependencies": {
120
122
  "react": "*",
@@ -47,7 +47,10 @@ if (isNewArchitectureEnabled()) {
47
47
  // levels up, in the package root.
48
48
  tasks.named('generateCodegenSchemaFromJavaScript').configure {
49
49
  jsRootDir.set(file("${packageRoot}/react-native/src"))
50
- jsInputFiles.set(fileTree("${packageRoot}/react-native/src") { include '**/*.ts', '**/*.tsx' })
50
+ // A property in newer React Native; in 0.76 and earlier, a file tree that follows jsRootDir
51
+ if (jsInputFiles instanceof org.gradle.api.provider.Property) {
52
+ jsInputFiles.set(fileTree("${packageRoot}/react-native/src") { include '**/*.ts', '**/*.tsx' })
53
+ }
51
54
  }
52
55
  tasks.named('generateCodegenArtifactsFromSchema').configure {
53
56
  packageJsonFile.set(file("${packageRoot}/package.json"))
@@ -41,9 +41,9 @@ export interface AlphaVideoSource {
41
41
  /** Sent with every request for the URL, e.g. Authorization; given to a `dataSource` too */
42
42
  headers?: Record<string, string>;
43
43
  /**
44
- * iOS and Android: read the file through the data source the app registered under this name
45
- * (`AlphaVideoDataSources.register` on Android, `AlphaVideoDataSources.register(_:name:)`
46
- * on iOS) instead of the built-in fetcher.
44
+ * Read the file through the data source registered under this name instead of the built-in
45
+ * fetcher: range-source's `RangeDataSources` on Android and iOS, and range-source-web's on the
46
+ * web. MediaCache registers as `'mediacache'` with `cache.registerRangeDataSource()`.
47
47
  */
48
48
  dataSource?: string;
49
49
  }
@@ -4,9 +4,10 @@
4
4
  * in WebGL2, loading those engines only when a file needs them.
5
5
  */
6
6
 
7
- import React, { forwardRef } from 'react';
7
+ import React, { forwardRef, useEffect, useMemo } from 'react';
8
8
  import { View, StyleSheet } from 'react-native';
9
9
  import { AlphaVideo } from 'alpha-video-web/react';
10
+ import type { ByteSource } from 'alpha-video-web';
10
11
  import { isUriSource, type AlphaVideoViewProps, type AlphaVideoViewRef } from './AlphaVideoView';
11
12
 
12
13
  /** asset://path names the app's bundled media; on the web it is served from the site root. */
@@ -14,14 +15,37 @@ function toWebUrl(uri: string): string {
14
15
  return uri.startsWith('asset://') ? '/' + uri.slice('asset://'.length).replace(/^\/+/, '') : uri;
15
16
  }
16
17
 
18
+ type DataSourceFactory = (uri: string, init?: { headers?: Record<string, string> }) => ByteSource;
19
+
20
+ /** range-source-web's `RangeDataSources`, read from its global symbol so this package needn't depend on it. */
21
+ function registeredDataSource(name: string): DataSourceFactory | undefined {
22
+ const scope = globalThis as { [key: symbol]: Map<string, DataSourceFactory> | undefined };
23
+ return scope[Symbol.for('range-source.dataSources')]?.get(name);
24
+ }
25
+
17
26
  export const WebPlayer = forwardRef<AlphaVideoViewRef, AlphaVideoViewProps>((props, ref) => {
18
27
  const { source, autoPlay = true, loop = true, paused = false, playbackSpeed = 1, webDecoder = 'auto', cacheKey } = props;
19
28
  const { storage, maxStorageBytes } = props;
20
29
  const { style, onLoad, onError, onEnd } = props;
21
30
  // Streamed by range from http(s) URLs and kept in the browser's storage (OPFS, else IndexedDB)
22
- // by the canvas engines; <video> uses the HTTP cache. A Blob or byte source plays as it is.
31
+ // by the canvas engines; <video> uses the HTTP cache. A Blob or byte source plays as it is, and
32
+ // a `dataSource` name plays the byte source registered under it, which keeps the bytes itself.
23
33
  const uriSource = isUriSource(source) ? source : undefined;
24
- const src = isUriSource(source) ? (source.uri ? toWebUrl(source.uri) : undefined) : source;
34
+ const dataSource = uriSource?.dataSource;
35
+ const headersKey = JSON.stringify(uriSource?.headers ?? null);
36
+ const fromDataSource = useMemo(
37
+ () => (dataSource && uriSource?.uri ? registeredDataSource(dataSource)?.(uriSource.uri, { headers: uriSource.headers }) ?? null : null),
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
39
+ [dataSource, uriSource?.uri, headersKey],
40
+ );
41
+ const src = dataSource ? fromDataSource : isUriSource(source) ? (source.uri ? toWebUrl(source.uri) : undefined) : source;
42
+
43
+ useEffect(() => {
44
+ if (dataSource && uriSource?.uri && !fromDataSource) {
45
+ onError?.({ error: `No data source is registered on the web under "${dataSource}"` });
46
+ }
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, [fromDataSource, dataSource, uriSource?.uri]);
25
49
 
26
50
  return (
27
51
  <View style={[styles.container, style]}>
@@ -34,8 +58,11 @@ export const WebPlayer = forwardRef<AlphaVideoViewRef, AlphaVideoViewProps>((pro
34
58
  paused={paused}
35
59
  playbackSpeed={playbackSpeed}
36
60
  decoder={webDecoder}
37
- headers={uriSource?.headers}
38
- cacheKey={uriSource?.cacheKey || cacheKey}
61
+ headers={dataSource ? undefined : uriSource?.headers}
62
+ cacheKey={
63
+ // As on Android and iOS, a data source's bytes are stored only when storage is set
64
+ dataSource && storage !== 'disk' && storage !== 'memory' ? undefined : uriSource?.cacheKey || cacheKey || (dataSource ? uriSource?.uri : undefined)
65
+ }
39
66
  storage={storage === 'disk' ? 'auto' : storage}
40
67
  maxStorageBytes={maxStorageBytes}
41
68
  style={fill}