react-native-alpha-video-gpu 0.0.0-stage → 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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +953 -2
  3. package/THIRD_PARTY_NOTICES.md +14 -0
  4. package/android/src/main/AndroidManifest.xml +9 -0
  5. package/android/src/main/cpp/CMakeLists.txt +41 -0
  6. package/android/src/main/cpp/alphavideogpu_jni.cpp +1168 -0
  7. package/android/src/main/cpp/astc_decomp.cpp +1550 -0
  8. package/android/src/main/cpp/astc_decomp.h +41 -0
  9. package/android/src/main/cpp/source_jni.cpp +242 -0
  10. package/android/src/main/cpp/source_jni.h +13 -0
  11. package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuNative.java +43 -0
  12. package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuReader.java +209 -0
  13. package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuSource.java +328 -0
  14. package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuView.java +2126 -0
  15. package/android/src/main/java/dev/mediacache/alphavideo/gpu/Fetcher.java +37 -0
  16. package/android/src/main/java/dev/mediacache/alphavideo/gpu/HttpFetcher.java +156 -0
  17. package/android/src/main/java/dev/mediacache/alphavideo/gpu/Ktx2Metadata.java +78 -0
  18. package/android/src/main/java/dev/mediacache/alphavideo/gpu/RangeSourceLink.java +54 -0
  19. package/android/src/main/java/dev/mediacache/alphavideo/gpu/ZstdSeekTable.java +120 -0
  20. package/android/src/rangesource/java/dev/mediacache/alphavideo/gpu/AppDataSource.java +50 -0
  21. package/ios/Sources/AlphaVideoGPU/AlphaVideoGpuSource.h +63 -0
  22. package/ios/Sources/AlphaVideoGPU/AlphaVideoGpuSource.mm +522 -0
  23. package/ios/Sources/AlphaVideoGPU/AlphaVideoGpuView.mm +2722 -0
  24. package/ios/Sources/AlphaVideoGPU/astc_decomp.cpp +1550 -0
  25. package/ios/Sources/AlphaVideoGPU/astc_decomp.h +41 -0
  26. package/ios/Sources/AlphaVideoGPU/include/AlphaVideoGPU.h +110 -0
  27. package/ios/Sources/AlphaVideoGPU/zstd_seek.h +250 -0
  28. package/package.json +78 -4
  29. package/react-native/android/build.gradle +95 -0
  30. package/react-native/android/src/main/AndroidManifest.xml +9 -0
  31. package/react-native/android/src/main/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuPackage.java +26 -0
  32. package/react-native/android/src/main/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuViewManager.java +187 -0
  33. package/react-native/android/src/main/java/dev/mediacache/alphavideo/gpu/reactnative/PlayerEventForwarder.java +107 -0
  34. package/react-native/android/src/newarch/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuViewManagerSpec.java +23 -0
  35. package/react-native/android/src/norangesource/java/dev/mediacache/alphavideo/gpu/AppDataSource.java +15 -0
  36. package/react-native/android/src/oldarch/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuViewManagerSpec.java +56 -0
  37. package/react-native/ios/RNAlphaVideoGpuViewComponentView.mm +176 -0
  38. package/react-native/ios/RNAlphaVideoGpuViewManager.mm +175 -0
  39. package/react-native/src/AlphaVideoGpuView.js +2 -0
  40. package/react-native/src/AlphaVideoGpuView.web.tsx +296 -0
  41. package/react-native/src/AlphaVideoGpuViewNativeComponent.ts +128 -0
  42. package/react-native/src/AlphaVideoGpuViewNativeComponent.web.ts +2 -0
  43. package/react-native/src/index.d.ts +95 -0
  44. package/react-native/src/index.js +162 -0
  45. package/react-native-alpha-video-gpu.podspec +37 -0
  46. package/react-native.config.js +12 -0
  47. package/third_party/licenses/Apache-2.0.txt +208 -0
  48. package/third_party/range-source/LICENSE +21 -0
  49. package/third_party/range-source/README.md +10 -0
  50. package/third_party/range-source/include/range_source.h +395 -0
  51. package/third_party/range-source/include/range_source_download.h +309 -0
  52. package/third_party/zstd/LICENSE +30 -0
  53. package/third_party/zstd/zstd.c +38717 -0
  54. package/third_party/zstd/zstd.h +2450 -0
@@ -0,0 +1,175 @@
1
+ //
2
+ // RNAlphaVideoGpuViewManager.mm
3
+ // React Native view manager for AlphaVideoGpuView (old architecture; the new one uses
4
+ // RNAlphaVideoGpuViewComponentView.mm)
5
+ //
6
+
7
+ #ifndef RCT_NEW_ARCH_ENABLED
8
+
9
+ #import <React/RCTUIManager.h>
10
+ #import <React/RCTViewManager.h>
11
+ #import "AlphaVideoGPU.h"
12
+
13
+ #pragma mark - RNAlphaVideoGpuView
14
+
15
+ /** AlphaVideoGpuView with the React Native props and events. */
16
+ @interface RNAlphaVideoGpuView : AlphaVideoGpuView
17
+
18
+ // `source` prop: { uri, cacheKey, headers?: [name, value, ...], dataSource? }
19
+ @property (nonatomic, copy) NSDictionary *reactSource;
20
+
21
+ @property (nonatomic, copy) RCTDirectEventBlock reactOnLoad;
22
+ @property (nonatomic, copy) RCTDirectEventBlock reactOnFrameUpdate;
23
+ @property (nonatomic, copy) RCTDirectEventBlock reactOnStateChange;
24
+ @property (nonatomic, copy) RCTDirectEventBlock reactOnFinished;
25
+ @property (nonatomic, copy) RCTDirectEventBlock reactOnError;
26
+
27
+ @end
28
+
29
+ @implementation RNAlphaVideoGpuView
30
+
31
+ - (instancetype)initWithFrame:(CGRect)frame
32
+ {
33
+ self = [super initWithFrame:frame];
34
+ if (self) {
35
+ [self forwardEvents];
36
+ }
37
+ return self;
38
+ }
39
+
40
+ static NSString *RNAVGString(id value)
41
+ {
42
+ return [value isKindOfClass:[NSString class]] && [value length] > 0 ? value : nil;
43
+ }
44
+
45
+ - (void)setReactSource:(NSDictionary *)reactSource
46
+ {
47
+ _reactSource = [reactSource copy];
48
+ // Adopt a cacheKey inside `source` before loading: React gives no ordering guarantee against
49
+ // the separate `cacheKey` prop, and the load reads the key to name what it stores.
50
+ NSString *cacheKey = RNAVGString(reactSource[@"cacheKey"]);
51
+ if (cacheKey) {
52
+ self.cacheKey = cacheKey;
53
+ }
54
+ NSArray *flat = [reactSource[@"headers"] isKindOfClass:[NSArray class]] ? reactSource[@"headers"] : @[];
55
+ NSMutableDictionary<NSString *, NSString *> *headers = [NSMutableDictionary dictionary];
56
+ for (NSUInteger i = 0; i + 1 < flat.count; i += 2) {
57
+ if ([flat[i] isKindOfClass:[NSString class]] && [flat[i + 1] isKindOfClass:[NSString class]]) {
58
+ headers[flat[i]] = flat[i + 1];
59
+ }
60
+ }
61
+ self.headers = headers;
62
+ self.dataSource = RNAVGString(reactSource[@"dataSource"]);
63
+ self.source = RNAVGString(reactSource[@"uri"]);
64
+ }
65
+
66
+ - (void)forwardEvents
67
+ {
68
+ __weak RNAlphaVideoGpuView *weakSelf = self;
69
+ self.onLoad = ^(NSInteger width, NSInteger height, NSInteger frameCount, CGFloat frameRate) {
70
+ RCTDirectEventBlock block = weakSelf.reactOnLoad;
71
+ if (!block) return;
72
+ block(@{
73
+ @"width": @(width),
74
+ @"height": @(height),
75
+ @"frameCount": @(frameCount),
76
+ @"frameRate": @(frameRate),
77
+ @"duration": @(frameCount > 0 && frameRate > 0 ? frameCount / frameRate : 0)
78
+ });
79
+ };
80
+ self.onFrameUpdate = ^(NSInteger frame, NSInteger totalFrames) {
81
+ RCTDirectEventBlock block = weakSelf.reactOnFrameUpdate;
82
+ if (block) block(@{@"frame": @(frame), @"totalFrames": @(totalFrames)});
83
+ };
84
+ self.onStateChange = ^(NSString *state) {
85
+ RCTDirectEventBlock block = weakSelf.reactOnStateChange;
86
+ if (block) block(@{@"state": state});
87
+ };
88
+ self.onFinished = ^{
89
+ RCTDirectEventBlock block = weakSelf.reactOnFinished;
90
+ if (block) block(@{});
91
+ };
92
+ self.onError = ^(NSString *error, NSString *code) {
93
+ RCTDirectEventBlock block = weakSelf.reactOnError;
94
+ if (block) block(code ? @{@"error": error, @"code": code} : @{@"error": error});
95
+ };
96
+ }
97
+
98
+ @end
99
+
100
+ #pragma mark - RNAlphaVideoGpuViewManager
101
+
102
+ @interface RNAlphaVideoGpuViewManager : RCTViewManager
103
+ @end
104
+
105
+ @implementation RNAlphaVideoGpuViewManager
106
+
107
+ RCT_EXPORT_MODULE(AlphaVideoGpuView)
108
+
109
+ + (BOOL)requiresMainQueueSetup
110
+ {
111
+ return YES;
112
+ }
113
+
114
+ - (UIView *)view
115
+ {
116
+ return [[RNAlphaVideoGpuView alloc] initWithFrame:CGRectZero];
117
+ }
118
+
119
+ RCT_REMAP_VIEW_PROPERTY(source, reactSource, NSDictionary)
120
+ RCT_REMAP_VIEW_PROPERTY(paused, playbackPaused, BOOL)
121
+ RCT_EXPORT_VIEW_PROPERTY(cacheKey, NSString)
122
+ RCT_EXPORT_VIEW_PROPERTY(storage, NSString)
123
+ RCT_EXPORT_VIEW_PROPERTY(maxStorageBytes, int64_t)
124
+ RCT_EXPORT_VIEW_PROPERTY(autoPlay, BOOL)
125
+ RCT_EXPORT_VIEW_PROPERTY(loop, BOOL)
126
+ RCT_EXPORT_VIEW_PROPERTY(playbackSpeed, CGFloat)
127
+ RCT_EXPORT_VIEW_PROPERTY(enableFrameEvents, BOOL)
128
+ RCT_EXPORT_VIEW_PROPERTY(streamingPrefetchAhead, NSInteger)
129
+ RCT_EXPORT_VIEW_PROPERTY(memoryStrategy, NSString)
130
+ RCT_EXPORT_VIEW_PROPERTY(stream, BOOL)
131
+ RCT_EXPORT_VIEW_PROPERTY(verbose, BOOL)
132
+ RCT_REMAP_VIEW_PROPERTY(onLoad, reactOnLoad, RCTDirectEventBlock)
133
+ RCT_REMAP_VIEW_PROPERTY(onFrameUpdate, reactOnFrameUpdate, RCTDirectEventBlock)
134
+ RCT_REMAP_VIEW_PROPERTY(onStateChange, reactOnStateChange, RCTDirectEventBlock)
135
+ RCT_REMAP_VIEW_PROPERTY(onFinished, reactOnFinished, RCTDirectEventBlock)
136
+ RCT_REMAP_VIEW_PROPERTY(onError, reactOnError, RCTDirectEventBlock)
137
+
138
+ // The block's view registry is empty under the new architecture's interop layer, so look the
139
+ // view up by tag instead.
140
+ - (void)withView:(NSNumber *)reactTag run:(void (^)(AlphaVideoGpuView *view))body
141
+ {
142
+ [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
143
+ UIView *view = [uiManager viewForReactTag:reactTag];
144
+ if ([view isKindOfClass:[AlphaVideoGpuView class]]) body((AlphaVideoGpuView *)view);
145
+ }];
146
+ }
147
+
148
+ RCT_EXPORT_METHOD(play:(nonnull NSNumber *)reactTag)
149
+ {
150
+ [self withView:reactTag run:^(AlphaVideoGpuView *view) { [view play]; }];
151
+ }
152
+
153
+ RCT_EXPORT_METHOD(pause:(nonnull NSNumber *)reactTag)
154
+ {
155
+ [self withView:reactTag run:^(AlphaVideoGpuView *view) { [view pause]; }];
156
+ }
157
+
158
+ RCT_EXPORT_METHOD(stop:(nonnull NSNumber *)reactTag)
159
+ {
160
+ [self withView:reactTag run:^(AlphaVideoGpuView *view) { [view stop]; }];
161
+ }
162
+
163
+ RCT_EXPORT_METHOD(seekToFrame:(nonnull NSNumber *)reactTag frame:(nonnull NSNumber *)frame)
164
+ {
165
+ [self withView:reactTag run:^(AlphaVideoGpuView *view) { [view seekToFrame:frame.integerValue]; }];
166
+ }
167
+
168
+ RCT_EXPORT_METHOD(seekToTime:(nonnull NSNumber *)reactTag seconds:(nonnull NSNumber *)seconds)
169
+ {
170
+ [self withView:reactTag run:^(AlphaVideoGpuView *view) { [view seekToTime:seconds.doubleValue]; }];
171
+ }
172
+
173
+ @end
174
+
175
+ #endif
@@ -0,0 +1,2 @@
1
+ // Native platforms resolve here instead of AlphaVideoGpuView.web.tsx, so alpha-video-gpu-web stays out of native bundles.
2
+ export default null;
@@ -0,0 +1,296 @@
1
+ /**
2
+ * AlphaVideoGpuView - React Native Web Implementation
3
+ *
4
+ * GPU-accelerated transparent alpha video player.
5
+ * Thin wrapper around AlphaVideoGpuCore (alpha-video-gpu-web) for React Native Web.
6
+ * Same API as native iOS/Android implementations.
7
+ *
8
+ * The zstd decoder (JS + WASM) comes bundled in alpha-video-gpu-web.
9
+ */
10
+
11
+ import React, { forwardRef, useImperativeHandle, useRef, useEffect, useMemo } from 'react';
12
+ import { View, StyleSheet } from 'react-native';
13
+
14
+ import {
15
+ AlphaVideoGpuCore,
16
+ ZSTDDecoder,
17
+ type AlphaVideoGpuError,
18
+ type AlphaVideoGpuErrorCode,
19
+ type AlphaVideoGpuLoadEvent,
20
+ type ByteSource,
21
+ } from 'alpha-video-gpu-web';
22
+
23
+ export type { AlphaVideoGpuErrorCode, AlphaVideoGpuLoadEvent, PlayerState, ByteSource } from 'alpha-video-gpu-web';
24
+
25
+ /**
26
+ * Where to load from (on web, `asset://` and http(s) URLs apply):
27
+ * - `https://…` / `http://…`: read by range as it plays, and kept as `storage` says (on web in the
28
+ * browser's storage, OPFS, else IndexedDB)
29
+ * - `file:///abs/path` or `/abs/path`: read in place
30
+ * - `asset://relative/path`: Android APK `assets/`, the iOS app bundle, or the site root on web
31
+ * - Android only: `content://…`, or a res/raw name (what `require()` resolves to in a release build)
32
+ *
33
+ * On web, `source` can also be a Blob or File, or a ByteSource that reads by offset.
34
+ */
35
+ export interface SourceType {
36
+ uri?: string;
37
+ /** What the store keeps the source under, for URLs that change while the file doesn't (presigned URLs) */
38
+ cacheKey?: string;
39
+ /** Sent with every request for the URL, e.g. Authorization; given to a `dataSource` too */
40
+ headers?: Record<string, string>;
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
+ */
46
+ dataSource?: string;
47
+ }
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
+
57
+ export interface AlphaVideoGpuViewProps {
58
+ source?: SourceType | string | Blob | ByteSource | null;
59
+ autoPlay?: boolean;
60
+ loop?: boolean;
61
+ playbackSpeed?: number;
62
+ /** Pause playback and decompression (useful for screen focus/blur) */
63
+ paused?: boolean;
64
+ cacheKey?: string;
65
+ /** Where a URL's bytes are kept between plays; on web 'disk' is the browser's storage. Read on mount */
66
+ storage?: 'auto' | 'disk' | 'memory' | 'none';
67
+ /** The store's cap in bytes (default 512 MB). Read on mount */
68
+ maxStorageBytes?: number;
69
+ style?: any;
70
+ testID?: string;
71
+ // Events have the native shape, so the same handlers work on every platform
72
+ /** Called when file metadata is loaded, provides dimensions and frame info */
73
+ onLoad?: (event: { nativeEvent: AlphaVideoGpuLoadEvent }) => void;
74
+ onStateChange?: (event: { nativeEvent: { state: string } }) => void;
75
+ onFrameUpdate?: (event: { nativeEvent: { frame: number; totalFrames: number } }) => void;
76
+ onFinished?: (event: { nativeEvent: {} }) => void;
77
+ /** `code` is set for the errors a page may want to handle, such as 'ASTC_UNSUPPORTED' */
78
+ onError?: (event: { nativeEvent: { error: string; code?: AlphaVideoGpuErrorCode } }) => void;
79
+ /** Send onFrameUpdate for every frame played; seeks always send it. Default: false */
80
+ enableFrameEvents?: boolean;
81
+
82
+ // Streaming parameters (with stream)
83
+ /** Frames decoded ahead of the shown one, all streaming keeps. Default: 8 */
84
+ streamingPrefetchAhead?: number;
85
+ /** Memory strategy: 'vram' | 'ram'. Default: 'vram' */
86
+ memoryStrategy?: 'vram' | 'ram';
87
+ /** Enable streaming mode for texture array playback. Default: true */
88
+ stream?: boolean;
89
+ /** Log info traces to the console. Default: false */
90
+ verbose?: boolean;
91
+ }
92
+
93
+ // ============================================================================
94
+ // AlphaVideoGpuView Component — Thin React Native Web wrapper
95
+ // ============================================================================
96
+
97
+ export interface AlphaVideoGpuViewRef {
98
+ play: () => void;
99
+ pause: () => void;
100
+ stop: () => void;
101
+ seekToFrame: (frame: number) => void;
102
+ seekToTime: (seconds: number) => void;
103
+ }
104
+
105
+ export const AlphaVideoGpuView = forwardRef<AlphaVideoGpuViewRef, AlphaVideoGpuViewProps>(({
106
+ source,
107
+ autoPlay = true,
108
+ loop = true,
109
+ playbackSpeed = 1.0,
110
+ paused = false,
111
+ cacheKey,
112
+ storage,
113
+ maxStorageBytes,
114
+ style,
115
+ testID,
116
+ onLoad,
117
+ onStateChange,
118
+ onFrameUpdate,
119
+ onFinished,
120
+ onError,
121
+ streamingPrefetchAhead = 8,
122
+ memoryStrategy = 'vram',
123
+ stream = true,
124
+ enableFrameEvents = false,
125
+ verbose = false,
126
+ }, ref) => {
127
+ const canvasRef = useRef<HTMLCanvasElement>(null);
128
+ const coreRef = useRef<AlphaVideoGpuCore | null>(null);
129
+ const loadedSourceRef = useRef<unknown>(null);
130
+ const callbacksRef = useRef({ onLoad, onStateChange, onFrameUpdate, onFinished, onError });
131
+ callbacksRef.current = { onLoad, onStateChange, onFrameUpdate, onFinished, onError };
132
+
133
+ useImperativeHandle(ref, () => ({
134
+ play: () => coreRef.current?.play(),
135
+ pause: () => coreRef.current?.pause(),
136
+ stop: () => coreRef.current?.stop(),
137
+ seekToFrame: (frame: number) => coreRef.current?.seekToFrame(frame),
138
+ seekToTime: (seconds: number) => coreRef.current?.seekToTime(seconds),
139
+ }), []);
140
+
141
+ // A Blob or ByteSource plays as it is; anything else is normalized to { uri } format
142
+ const directSource = isDirectSource(source) ? source : null;
143
+ const normalizedSource = useMemo((): SourceType | null => {
144
+ if (!source || isDirectSource(source)) return null;
145
+ if (typeof source === 'string') return { uri: source };
146
+ return source;
147
+ }, [source]);
148
+ const headersRef = useRef(normalizedSource?.headers);
149
+ headersRef.current = normalizedSource?.headers;
150
+
151
+ // Create core instance when canvas is available
152
+ useEffect(() => {
153
+ const canvas = canvasRef.current;
154
+ if (!canvas || coreRef.current) return;
155
+
156
+ coreRef.current = new AlphaVideoGpuCore(canvas, {
157
+ autoPlay,
158
+ loop,
159
+ playbackSpeed,
160
+ stream,
161
+ streamingPrefetchAhead,
162
+ memoryStrategy,
163
+ verbose,
164
+ frameEvents: enableFrameEvents,
165
+ storage: storage === 'disk' ? 'auto' : storage,
166
+ maxStorageBytes: maxStorageBytes && maxStorageBytes > 0 ? maxStorageBytes : undefined,
167
+ ZSTDDecoderClass: ZSTDDecoder,
168
+ onStateChange: state => callbacksRef.current.onStateChange?.({ nativeEvent: { state } }),
169
+ onLoad: event => callbacksRef.current.onLoad?.({ nativeEvent: event }),
170
+ onFrameUpdate: (frame, totalFrames) =>
171
+ callbacksRef.current.onFrameUpdate?.({ nativeEvent: { frame, totalFrames } }),
172
+ onFinished: () => callbacksRef.current.onFinished?.({ nativeEvent: {} }),
173
+ onError: error => {
174
+ const { code } = error as AlphaVideoGpuError;
175
+ callbacksRef.current.onError?.({ nativeEvent: code ? { error: error.message, code } : { error: error.message } });
176
+ },
177
+ });
178
+
179
+ return () => {
180
+ coreRef.current?.destroy();
181
+ coreRef.current = null;
182
+ loadedSourceRef.current = null;
183
+ };
184
+ // Only create/destroy on mount/unmount
185
+ // eslint-disable-next-line react-hooks/exhaustive-deps
186
+ }, []);
187
+
188
+ // What identifies "the same video". A presigned URL is re-signed every time the app
189
+ // lists its media, so the URL changes while still addressing the same object; keying
190
+ // the load on it would resetSource() and refetch on each refresh, visibly restarting
191
+ // a loop that should run until unmount. The cache key is the stable identity.
192
+ const sourceCacheKey = normalizedSource?.cacheKey || cacheKey;
193
+ const dataSource = normalizedSource?.dataSource;
194
+ const keyed = sourceCacheKey || normalizedSource?.uri;
195
+ const sourceIdentity = directSource || (keyed && dataSource ? `${dataSource}:${keyed}` : keyed) || null;
196
+
197
+ // Load source when it changes
198
+ useEffect(() => {
199
+ const core = coreRef.current;
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
+ }
218
+
219
+ const toLoad = directSource || uri;
220
+ if (!toLoad) return;
221
+ loadedSourceRef.current = sourceIdentity;
222
+ core.setSource(toLoad, { cacheKey: sourceCacheKey, headers: headersRef.current });
223
+
224
+ return () => {
225
+ loadedSourceRef.current = null;
226
+ core.resetSource();
227
+ };
228
+ // eslint-disable-next-line react-hooks/exhaustive-deps
229
+ }, [sourceIdentity]);
230
+
231
+ useEffect(() => {
232
+ if (coreRef.current) {
233
+ coreRef.current.verbose = verbose;
234
+ }
235
+ }, [verbose]);
236
+
237
+ useEffect(() => {
238
+ if (coreRef.current) {
239
+ coreRef.current.frameEvents = enableFrameEvents;
240
+ }
241
+ }, [enableFrameEvents]);
242
+
243
+ // Handle playback speed changes
244
+ useEffect(() => {
245
+ if (coreRef.current) {
246
+ coreRef.current.playbackSpeed = playbackSpeed;
247
+ }
248
+ }, [playbackSpeed]);
249
+
250
+ // Handle loop changes
251
+ useEffect(() => {
252
+ if (coreRef.current) {
253
+ coreRef.current.loop = loop;
254
+ }
255
+ }, [loop]);
256
+
257
+ // Handle paused prop changes
258
+ useEffect(() => {
259
+ coreRef.current?.setPaused(paused);
260
+ }, [paused]);
261
+
262
+ // Get canvas dimensions from style
263
+ const canvasStyle = useMemo(() => {
264
+ const flatStyle = StyleSheet.flatten(style) || {};
265
+ return {
266
+ width: (flatStyle as any).width || 300,
267
+ height: (flatStyle as any).height || 300,
268
+ };
269
+ }, [style]);
270
+
271
+ return (
272
+ <View testID={testID} style={[styles.container, style]}>
273
+ <canvas
274
+ ref={canvasRef}
275
+ width={canvasStyle.width}
276
+ height={canvasStyle.height}
277
+ style={{ width: '100%', height: '100%' }}
278
+ />
279
+ </View>
280
+ );
281
+ });
282
+
283
+ function isDirectSource(source: AlphaVideoGpuViewProps['source']): source is Blob | ByteSource {
284
+ if (!source || typeof source !== 'object') return false;
285
+ return (typeof Blob !== 'undefined' && source instanceof Blob) || typeof (source as ByteSource).read === 'function';
286
+ }
287
+
288
+ const styles = StyleSheet.create({
289
+ container: {
290
+ overflow: 'hidden',
291
+ },
292
+ });
293
+
294
+ AlphaVideoGpuView.displayName = 'AlphaVideoGpuView';
295
+
296
+ export default AlphaVideoGpuView;
@@ -0,0 +1,128 @@
1
+ import type * as React from 'react';
2
+ import type { HostComponent, ViewProps } from 'react-native';
3
+ import type {
4
+ DirectEventHandler,
5
+ Double,
6
+ Float,
7
+ Int32,
8
+ WithDefault,
9
+ } from 'react-native/Libraries/Types/CodegenTypes';
10
+ import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
11
+ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
12
+
13
+ /**
14
+ * Where to load from:
15
+ * - `https://…` / `http://…`: with react-native-range-source read by range as it plays, and kept
16
+ * as `storage` says; without it downloaded whole
17
+ * - `file:///abs/path` or `/abs/path`: read in place
18
+ * - `asset://relative/path`: Android APK `assets/`, the iOS app bundle, or the site root on web
19
+ * - Android only: `content://…`, or a res/raw name (what `require()` resolves to in a release build)
20
+ * - with `dataSource`, any URI that data source reads
21
+ */
22
+ export interface SourceType {
23
+ uri?: string;
24
+ /** What the store keeps the source under, for URLs that change while the file doesn't (presigned URLs) */
25
+ cacheKey?: string;
26
+ /** Sent with every request for the URL, e.g. Authorization; given to a `dataSource` too */
27
+ headers?: Record<string, string>;
28
+ /** A data source registered in native code under this name (react-native-range-source's RangeDataSources) */
29
+ dataSource?: string;
30
+ }
31
+
32
+ export interface AlphaVideoGpuLoadEvent {
33
+ width: number;
34
+ height: number;
35
+ frameCount: number;
36
+ frameRate: number;
37
+ duration: number;
38
+ }
39
+
40
+ type NativeSource = Readonly<{
41
+ uri?: string;
42
+ cacheKey?: string;
43
+ /** Request headers as name, value, name, value... (codegen has no string maps) */
44
+ headers?: ReadonlyArray<string>;
45
+ dataSource?: string;
46
+ }>;
47
+
48
+ type LoadEvent = Readonly<{
49
+ width: Int32;
50
+ height: Int32;
51
+ frameCount: Int32;
52
+ frameRate: Double;
53
+ duration: Double;
54
+ }>;
55
+
56
+ type FrameUpdateEvent = Readonly<{
57
+ frame: Int32;
58
+ totalFrames: Int32;
59
+ }>;
60
+
61
+ type StateChangeEvent = Readonly<{
62
+ state: string;
63
+ }>;
64
+
65
+ type ErrorEvent = Readonly<{
66
+ error: string;
67
+ /** `'NO_SOURCE_MODULE'` for a data source without the range-source add-on */
68
+ code?: string;
69
+ }>;
70
+
71
+ export interface NativeProps extends ViewProps {
72
+ source?: NativeSource;
73
+ autoPlay?: WithDefault<boolean, true>;
74
+ loop?: WithDefault<boolean, true>;
75
+ playbackSpeed?: WithDefault<Float, 1.0>;
76
+ /** Pause playback and decompression (useful for screen focus/blur) */
77
+ paused?: WithDefault<boolean, false>;
78
+ cacheKey?: string;
79
+ /** Where a URL's or data source's bytes are kept between plays */
80
+ storage?: WithDefault<'auto' | 'disk' | 'memory' | 'none', 'auto'>;
81
+ /** The store's cap in bytes; 0 for the default (512 MB) */
82
+ maxStorageBytes?: WithDefault<Double, 0>;
83
+
84
+ // Streaming parameters (with stream)
85
+ /** Frames decoded ahead of the shown one, all streaming keeps: the GPU ring (vram) or the CPU cache (ram). Default: 8 */
86
+ streamingPrefetchAhead?: WithDefault<Int32, 8>;
87
+
88
+ /**
89
+ * Memory strategy for frame storage.
90
+ * - 'vram' (default): Store frames in GPU VRAM (texture array). Best for many simultaneous players.
91
+ * - 'ram': Store decoded frames in RAM, upload per-frame. Lower VRAM usage.
92
+ */
93
+ memoryStrategy?: WithDefault<'vram' | 'ram', 'vram'>;
94
+
95
+ /**
96
+ * Enable streaming mode for texture array playback.
97
+ * When true (default): Use streaming decompression with ring buffer (constant memory usage).
98
+ * When false: Load all frames at once.
99
+ */
100
+ stream?: WithDefault<boolean, true>;
101
+
102
+ /** Send onFrameUpdate for every rendered frame. Default: false */
103
+ enableFrameEvents?: WithDefault<boolean, false>;
104
+ /** Log info traces (Logcat / Xcode console). Warnings and errors always log. Default: false */
105
+ verbose?: WithDefault<boolean, false>;
106
+
107
+ onLoad?: DirectEventHandler<LoadEvent>;
108
+ onFrameUpdate?: DirectEventHandler<FrameUpdateEvent>;
109
+ onStateChange?: DirectEventHandler<StateChangeEvent>;
110
+ onFinished?: DirectEventHandler<Readonly<{}>>;
111
+ onError?: DirectEventHandler<ErrorEvent>;
112
+ }
113
+
114
+ type NativeComponentType = HostComponent<NativeProps>;
115
+
116
+ interface NativeCommands {
117
+ play: (viewRef: React.ElementRef<NativeComponentType>) => void;
118
+ pause: (viewRef: React.ElementRef<NativeComponentType>) => void;
119
+ stop: (viewRef: React.ElementRef<NativeComponentType>) => void;
120
+ seekToFrame: (viewRef: React.ElementRef<NativeComponentType>, frame: Int32) => void;
121
+ seekToTime: (viewRef: React.ElementRef<NativeComponentType>, seconds: Double) => void;
122
+ }
123
+
124
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
125
+ supportedCommands: ['play', 'pause', 'stop', 'seekToFrame', 'seekToTime'],
126
+ });
127
+
128
+ export default codegenNativeComponent<NativeProps>('AlphaVideoGpuView') as NativeComponentType;
@@ -0,0 +1,2 @@
1
+ // Web renders AlphaVideoGpuView.web.tsx. This stub keeps the native spec's imports out of web bundles.
2
+ export default null;