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 +20 -15
- package/android/build.gradle +6 -1
- package/android/src/main/java/dev/mediacache/alphavideo/AlphaVideoView.java +29 -13
- package/ios/Sources/AlphaVideo/AlphaVideoView.mm +2 -2
- package/ios/Sources/AlphaVideo/include/AlphaVideo.h +1 -1
- package/lib/commonjs/WebPlayer.web.js +27 -4
- package/lib/commonjs/WebPlayer.web.js.map +1 -1
- package/lib/module/WebPlayer.web.js +27 -4
- package/lib/module/WebPlayer.web.js.map +1 -1
- package/lib/typescript/react-native/src/AlphaVideoView.d.ts +3 -3
- package/lib/typescript/react-native/src/WebPlayer.web.d.ts.map +1 -1
- package/package.json +4 -2
- package/react-native/android/build.gradle +4 -1
- package/react-native/src/AlphaVideoView.tsx +3 -3
- package/react-native/src/WebPlayer.web.tsx +32 -5
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.
|
|
175
|
-
implementation 'dev.mediacache:alpha-video-vpx:1.0.
|
|
176
|
-
implementation 'dev.mediacache:alpha-video-av1:1.0.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
package/android/build.gradle
CHANGED
|
@@ -21,7 +21,7 @@ def safeExtGet(name, fallback) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
group = 'dev.mediacache'
|
|
24
|
-
version = '1.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
|
-
|
|
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
|
-
|
|
1041
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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:
|
|
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","
|
|
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
|
|
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:
|
|
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","
|
|
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
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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
|
|
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={
|
|
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}
|