react-native-alpha-video-gpu 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
|
@@ -12,7 +12,7 @@ Plays transparent (alpha) videos stored as ASTC texture arrays in KTX2 files, dr
|
|
|
12
12
|
|---|---|---|
|
|
13
13
|
| React Native (Android, iOS, web) | npm `react-native-alpha-video-gpu` | `AlphaVideoGpuView` |
|
|
14
14
|
| Android | Maven `dev.mediacache:alpha-video-gpu` | `dev.mediacache.alphavideo.gpu.AlphaVideoGpuView` |
|
|
15
|
-
| iOS | Swift Package Manager, product `AlphaVideoGPU
|
|
15
|
+
| iOS | Swift Package Manager, product `AlphaVideoGPU` (`AlphaVideoGPUCore` without range-source); CocoaPods `alpha-video-gpu` | `AlphaVideoGpuView` |
|
|
16
16
|
| Web (plain JS/TS, React) | npm `alpha-video-gpu-web` | `createAlphaVideoGpuPlayer`, `AlphaVideoGpuPlayer` |
|
|
17
17
|
|
|
18
18
|
See it next to the other formats in the [demo app](#demo-and-related-packages).
|
|
@@ -267,14 +267,14 @@ pod 'alpha-video-gpu', '1.0.0'
|
|
|
267
267
|
**Swift Package Manager**, package `alpha-video-gpu`, product `AlphaVideoGPU`:
|
|
268
268
|
|
|
269
269
|
```swift
|
|
270
|
-
.package(url: "https://github.com/MediaCacheDev/alpha-video-gpu", from: "1.0.
|
|
270
|
+
.package(url: "https://github.com/MediaCacheDev/alpha-video-gpu", from: "1.0.1")
|
|
271
271
|
// in the target's dependencies:
|
|
272
272
|
.product(name: "AlphaVideoGPU", package: "alpha-video-gpu")
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
-
The module is `AlphaVideoGPU` either way.
|
|
275
|
+
In an Xcode project, add the package (File > Add Package Dependencies) and pick `AlphaVideoGPU` for the app target. The module is `AlphaVideoGPU` either way.
|
|
276
276
|
|
|
277
|
-
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
|
|
277
|
+
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 `AlphaVideoGPU` product. To leave it out ([Without range-source](#without-range-source)), take the pod's core alone, `pod 'alpha-video-gpu', '1.0.0', :subspecs => ['Core']`, or the package's `AlphaVideoGPUCore` product instead of `AlphaVideoGPU`. These are products rather than a package trait because Xcode before 26.4 applies no package traits, default ones included.
|
|
278
278
|
|
|
279
279
|
`AlphaVideoGpuView` is an Objective-C `MTKView`, so it works from Swift and Objective-C:
|
|
280
280
|
|
|
@@ -364,7 +364,7 @@ Where the frames live:
|
|
|
364
364
|
| Form | iOS | Android | Web | Notes |
|
|
365
365
|
|------|-----|---------|-----|-------|
|
|
366
366
|
| `https://…`, `http://…` | ✓ | ✓ | ✓ | Read by range requests as it plays, and kept between plays; see [Streaming and storage](#streaming-and-storage). Without range-source on Android and iOS, downloaded whole. `source.headers` go with every request. |
|
|
367
|
-
| anything, with `source.dataSource` | ✓ | ✓ |
|
|
367
|
+
| anything, with `source.dataSource` | ✓ | ✓ | ✓ | Read by a data source the app registered under that name; see [Data sources](#data-sources). On react-native-web the name is registered with range-source-web; a web app holding the player passes a byte source instead (below). |
|
|
368
368
|
| `file:///abs/path`, `/abs/path` | ✓ | ✓ | — | Read in place. |
|
|
369
369
|
| `asset://path/in/bundle` | ✓ | ✓ | ✓ | Android: APK `assets/`. iOS: the app bundle's resource folder. Web: the site root (`/path/in/bundle`). |
|
|
370
370
|
| `content://…` | — | ✓ | — | Read through the `ContentResolver`. |
|
|
@@ -465,7 +465,11 @@ final class MyDataSource: NSObject, RangeDataSource {
|
|
|
465
465
|
RangeDataSources.register(MyDataSource(), name: "mine")
|
|
466
466
|
```
|
|
467
467
|
|
|
468
|
-
**React Native:** register in native code, as above, in `MainApplication` (Android) and `AppDelegate` (iOS, `import RangeSource`).
|
|
468
|
+
**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))`.
|
|
469
|
+
|
|
470
|
+
**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.
|
|
471
|
+
|
|
472
|
+
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.
|
|
469
473
|
|
|
470
474
|
#### Bundled with `require()`
|
|
471
475
|
|
|
@@ -835,7 +839,7 @@ Not ideal for:
|
|
|
835
839
|
|
|
836
840
|
| Prop | Type | Default | Description |
|
|
837
841
|
|------|------|---------|-------------|
|
|
838
|
-
| `source` | `{ uri: string, cacheKey?: string, headers?: Record<string, string>, dataSource?: string }` | — | What to play; see [Sources](#sources). On web also a `Blob` or a byte source, and `dataSource`
|
|
842
|
+
| `source` | `{ uri: string, cacheKey?: string, headers?: Record<string, string>, dataSource?: string }` | — | What to play; see [Sources](#sources). On web also a `Blob` or a byte source, and `dataSource` names a data source registered with range-source-web |
|
|
839
843
|
| `style` | `ViewStyle` | — | Container style |
|
|
840
844
|
| `autoPlay` | `boolean` | `true` | Start playback once loaded |
|
|
841
845
|
| `loop` | `boolean` | `true` | Loop playback |
|
|
@@ -890,7 +894,7 @@ The fixtures in `tests/fixtures/` are test cards from the tester app, encoded to
|
|
|
890
894
|
| Web, under Node | `cd web && npm test` | The KTX2 parser, the seek table, streaming reads and seeks in zstd and uncompressed files, and every frame of the fixtures through the software ASTC decoder, read back by its frame code |
|
|
891
895
|
| Android, on the host | `cd android && ./gradlew testReleaseUnitTest` | The seek table parser (JUnit) |
|
|
892
896
|
| iOS, on the host | `clang -O1 -Ithird_party -Iios/Sources/AlphaVideoGPU ios/Tests/zstd_seek_test.c third_party/zstd/zstd.c -o /tmp/zstd_seek_test && /tmp/zstd_seek_test` | The seek table, reads and seeks (`zstd_seek.h`), from the repository root |
|
|
893
|
-
| iOS simulator | `xcodebuild test -scheme AlphaVideoGPU -destination 'platform=iOS Simulator,name=iPhone 16 Pro'`, and the same with `-scheme WithoutRangeSource-Package` in `ios/Tests/WithoutRangeSource` | With range-source (`SourceTests`), a data source registered with it. Without it (a package
|
|
897
|
+
| iOS simulator | `xcodebuild test -scheme AlphaVideoGPU-Package -destination 'platform=iOS Simulator,name=iPhone 16 Pro'`, and the same with `-scheme WithoutRangeSource-Package` in `ios/Tests/WithoutRangeSource` | With range-source (`SourceTests`), a data source registered with it. Without it (a package taking the `AlphaVideoGPUCore` product), a file plays and a data source reports `NO_SOURCE_MODULE`. `RANGE_SOURCE_PATH` points them at a range-source checkout instead of its release |
|
|
894
898
|
| Web, in a browser | `web/test/browser/index.html`, see below | Every streaming and memory mode on every fixture in a real browser: frame codes after seeks and in playback, from the canvas |
|
|
895
899
|
| Android and iOS devices | the tester app's `test:pixels` and `test:streaming` | Every mode and memory strategy on every card, pixel by pixel, and streaming; see below |
|
|
896
900
|
|
|
@@ -921,7 +925,7 @@ The tester app installs `react-native-range-source`. Without it (`npm uninstall
|
|
|
921
925
|
(cd web && npm test)
|
|
922
926
|
(cd android && ./gradlew testReleaseUnitTest)
|
|
923
927
|
clang -O1 -Ithird_party -Iios/Sources/AlphaVideoGPU ios/Tests/zstd_seek_test.c third_party/zstd/zstd.c -o /tmp/zstd_seek_test && /tmp/zstd_seek_test
|
|
924
|
-
xcodebuild test -scheme AlphaVideoGPU -destination 'platform=iOS Simulator,name=iPhone 16 Pro'
|
|
928
|
+
xcodebuild test -scheme AlphaVideoGPU-Package -destination 'platform=iOS Simulator,name=iPhone 16 Pro'
|
|
925
929
|
(cd ios/Tests/WithoutRangeSource && xcodebuild test -scheme WithoutRangeSource-Package -destination 'platform=iOS Simulator,name=iPhone 16 Pro')
|
|
926
930
|
# the browser page, with ?astc=off too; then, in alpha-video-test: test:pixels and test:streaming with --player gpu on each platform
|
|
927
931
|
```
|
|
@@ -43,7 +43,7 @@ static void AVGWarnWithoutAddOn(void)
|
|
|
43
43
|
{
|
|
44
44
|
static dispatch_once_t once;
|
|
45
45
|
dispatch_once(&once, ^{
|
|
46
|
-
NSLog(@"[AlphaVideoGPU] range-source isn't installed (the
|
|
46
|
+
NSLog(@"[AlphaVideoGPU] range-source isn't installed (the AlphaVideoGPU SwiftPM product, the alpha-video-gpu pod's "
|
|
47
47
|
@"Source subspec, or for React Native npm i react-native-range-source): URLs are downloaded whole for each "
|
|
48
48
|
@"play, without range requests, storage, maxStorageBytes or cacheKey");
|
|
49
49
|
});
|
|
@@ -520,7 +520,7 @@ NSString *const AlphaVideoGpuErrorNoSourceModule = @"NO_SOURCE_MODULE";
|
|
|
520
520
|
if (readsRanges && !AVGRangeSourceAvailable()) {
|
|
521
521
|
if (dataSourceName) {
|
|
522
522
|
[self notifyError:[NSString stringWithFormat:@"The data source \"%@\" needs the range-source add-on (the "
|
|
523
|
-
@"
|
|
523
|
+
@"AlphaVideoGPU SwiftPM product, the alpha-video-gpu pod's Source "
|
|
524
524
|
@"subspec, or for React Native react-native-range-source), which "
|
|
525
525
|
@"the app doesn't include", dataSourceName]
|
|
526
526
|
code:AlphaVideoGpuErrorNoSourceModule];
|
|
@@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* onError's code for a data source without the range-source add-on, which reads them (the
|
|
15
|
-
*
|
|
15
|
+
* AlphaVideoGPU SwiftPM product, the alpha-video-gpu pod's Source subspec, or for React Native
|
|
16
16
|
* react-native-range-source). Without it a URL is downloaded whole for each play, and storage,
|
|
17
17
|
* maxStorageBytes and cacheKey are ignored.
|
|
18
18
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-alpha-video-gpu",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Transparent (alpha) animations for React Native on Android, iOS and web, from ASTC texture arrays in KTX2 files. The GPU draws the frames as stored, with Metal, OpenGL ES or WebGL, so dozens play at once with little CPU.",
|
|
5
5
|
"main": "react-native/src/index.js",
|
|
6
6
|
"types": "react-native/src/index.d.ts",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"LICENSE",
|
|
16
16
|
"THIRD_PARTY_NOTICES.md",
|
|
17
17
|
"!android/src/test",
|
|
18
|
+
"!ios/Sources/AlphaVideoGPUSource",
|
|
18
19
|
"!react-native/android/build",
|
|
19
20
|
"!react-native/android/.gradle",
|
|
20
21
|
"!react-native/android/.cxx",
|
|
@@ -30,7 +30,10 @@ if (isNewArchitectureEnabled()) {
|
|
|
30
30
|
// levels up, in the package root.
|
|
31
31
|
tasks.named('generateCodegenSchemaFromJavaScript').configure {
|
|
32
32
|
jsRootDir.set(file("${packageRoot}/react-native/src"))
|
|
33
|
-
|
|
33
|
+
// A property in newer React Native; in 0.76 and earlier, a file tree that follows jsRootDir
|
|
34
|
+
if (jsInputFiles instanceof org.gradle.api.provider.Property) {
|
|
35
|
+
jsInputFiles.set(fileTree("${packageRoot}/react-native/src") { include '**/*.ts', '**/*.tsx', '**/*.js' })
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
tasks.named('generateCodegenArtifactsFromSchema').configure {
|
|
36
39
|
packageJsonFile.set(file("${packageRoot}/package.json"))
|
|
@@ -38,10 +38,22 @@ export interface SourceType {
|
|
|
38
38
|
cacheKey?: string;
|
|
39
39
|
/** Sent with every request for the URL, e.g. Authorization; given to a `dataSource` too */
|
|
40
40
|
headers?: Record<string, string>;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Read the file through the data source registered under this name: range-source's
|
|
43
|
+
* `RangeDataSources` on Android and iOS, and range-source-web's on the web. MediaCache
|
|
44
|
+
* registers as `'mediacache'` with `cache.registerRangeDataSource()`.
|
|
45
|
+
*/
|
|
42
46
|
dataSource?: string;
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
type DataSourceFactory = (uri: string, init?: { headers?: Record<string, string> }) => ByteSource;
|
|
50
|
+
|
|
51
|
+
/** range-source-web's `RangeDataSources`, read from its global symbol so this package needn't depend on it. */
|
|
52
|
+
function registeredDataSource(name: string): DataSourceFactory | undefined {
|
|
53
|
+
const scope = globalThis as { [key: symbol]: Map<string, DataSourceFactory> | undefined };
|
|
54
|
+
return scope[Symbol.for('range-source.dataSources')]?.get(name);
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
export interface AlphaVideoGpuViewProps {
|
|
46
58
|
source?: SourceType | string | Blob | ByteSource | null;
|
|
47
59
|
autoPlay?: boolean;
|
|
@@ -178,14 +190,34 @@ export const AlphaVideoGpuView = forwardRef<AlphaVideoGpuViewRef, AlphaVideoGpuV
|
|
|
178
190
|
// the load on it would resetSource() and refetch on each refresh, visibly restarting
|
|
179
191
|
// a loop that should run until unmount. The cache key is the stable identity.
|
|
180
192
|
const sourceCacheKey = normalizedSource?.cacheKey || cacheKey;
|
|
181
|
-
const
|
|
193
|
+
const dataSource = normalizedSource?.dataSource;
|
|
194
|
+
const keyed = sourceCacheKey || normalizedSource?.uri;
|
|
195
|
+
const sourceIdentity = directSource || (keyed && dataSource ? `${dataSource}:${keyed}` : keyed) || null;
|
|
182
196
|
|
|
183
197
|
// Load source when it changes
|
|
184
198
|
useEffect(() => {
|
|
185
199
|
const core = coreRef.current;
|
|
186
|
-
const
|
|
187
|
-
if (!core || !
|
|
200
|
+
const uri = normalizedSource?.uri;
|
|
201
|
+
if (!core || !sourceIdentity || loadedSourceRef.current === sourceIdentity) return;
|
|
202
|
+
|
|
203
|
+
if (dataSource && uri) {
|
|
204
|
+
// The data source keeps the bytes; the player stores them too only when storage is set
|
|
205
|
+
const factory = registeredDataSource(dataSource);
|
|
206
|
+
if (!factory) {
|
|
207
|
+
callbacksRef.current.onError?.({ nativeEvent: { error: `No data source is registered on the web under "${dataSource}"` } });
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
loadedSourceRef.current = sourceIdentity;
|
|
211
|
+
const stored = storage === 'disk' || storage === 'memory';
|
|
212
|
+
core.setSource(factory(uri, { headers: headersRef.current }), stored ? { cacheKey: keyed } : {});
|
|
213
|
+
return () => {
|
|
214
|
+
loadedSourceRef.current = null;
|
|
215
|
+
core.resetSource();
|
|
216
|
+
};
|
|
217
|
+
}
|
|
188
218
|
|
|
219
|
+
const toLoad = directSource || uri;
|
|
220
|
+
if (!toLoad) return;
|
|
189
221
|
loadedSourceRef.current = sourceIdentity;
|
|
190
222
|
core.setSource(toLoad, { cacheKey: sourceCacheKey, headers: headersRef.current });
|
|
191
223
|
|