react-native-alpha-video-gpu 0.0.0-stage → 1.0.0

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 +949 -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 +77 -4
  29. package/react-native/android/build.gradle +92 -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 +264 -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,264 @@
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
+ /** iOS and Android: a data source registered in native code under this name */
42
+ dataSource?: string;
43
+ }
44
+
45
+ export interface AlphaVideoGpuViewProps {
46
+ source?: SourceType | string | Blob | ByteSource | null;
47
+ autoPlay?: boolean;
48
+ loop?: boolean;
49
+ playbackSpeed?: number;
50
+ /** Pause playback and decompression (useful for screen focus/blur) */
51
+ paused?: boolean;
52
+ cacheKey?: string;
53
+ /** Where a URL's bytes are kept between plays; on web 'disk' is the browser's storage. Read on mount */
54
+ storage?: 'auto' | 'disk' | 'memory' | 'none';
55
+ /** The store's cap in bytes (default 512 MB). Read on mount */
56
+ maxStorageBytes?: number;
57
+ style?: any;
58
+ testID?: string;
59
+ // Events have the native shape, so the same handlers work on every platform
60
+ /** Called when file metadata is loaded, provides dimensions and frame info */
61
+ onLoad?: (event: { nativeEvent: AlphaVideoGpuLoadEvent }) => void;
62
+ onStateChange?: (event: { nativeEvent: { state: string } }) => void;
63
+ onFrameUpdate?: (event: { nativeEvent: { frame: number; totalFrames: number } }) => void;
64
+ onFinished?: (event: { nativeEvent: {} }) => void;
65
+ /** `code` is set for the errors a page may want to handle, such as 'ASTC_UNSUPPORTED' */
66
+ onError?: (event: { nativeEvent: { error: string; code?: AlphaVideoGpuErrorCode } }) => void;
67
+ /** Send onFrameUpdate for every frame played; seeks always send it. Default: false */
68
+ enableFrameEvents?: boolean;
69
+
70
+ // Streaming parameters (with stream)
71
+ /** Frames decoded ahead of the shown one, all streaming keeps. Default: 8 */
72
+ streamingPrefetchAhead?: number;
73
+ /** Memory strategy: 'vram' | 'ram'. Default: 'vram' */
74
+ memoryStrategy?: 'vram' | 'ram';
75
+ /** Enable streaming mode for texture array playback. Default: true */
76
+ stream?: boolean;
77
+ /** Log info traces to the console. Default: false */
78
+ verbose?: boolean;
79
+ }
80
+
81
+ // ============================================================================
82
+ // AlphaVideoGpuView Component — Thin React Native Web wrapper
83
+ // ============================================================================
84
+
85
+ export interface AlphaVideoGpuViewRef {
86
+ play: () => void;
87
+ pause: () => void;
88
+ stop: () => void;
89
+ seekToFrame: (frame: number) => void;
90
+ seekToTime: (seconds: number) => void;
91
+ }
92
+
93
+ export const AlphaVideoGpuView = forwardRef<AlphaVideoGpuViewRef, AlphaVideoGpuViewProps>(({
94
+ source,
95
+ autoPlay = true,
96
+ loop = true,
97
+ playbackSpeed = 1.0,
98
+ paused = false,
99
+ cacheKey,
100
+ storage,
101
+ maxStorageBytes,
102
+ style,
103
+ testID,
104
+ onLoad,
105
+ onStateChange,
106
+ onFrameUpdate,
107
+ onFinished,
108
+ onError,
109
+ streamingPrefetchAhead = 8,
110
+ memoryStrategy = 'vram',
111
+ stream = true,
112
+ enableFrameEvents = false,
113
+ verbose = false,
114
+ }, ref) => {
115
+ const canvasRef = useRef<HTMLCanvasElement>(null);
116
+ const coreRef = useRef<AlphaVideoGpuCore | null>(null);
117
+ const loadedSourceRef = useRef<unknown>(null);
118
+ const callbacksRef = useRef({ onLoad, onStateChange, onFrameUpdate, onFinished, onError });
119
+ callbacksRef.current = { onLoad, onStateChange, onFrameUpdate, onFinished, onError };
120
+
121
+ useImperativeHandle(ref, () => ({
122
+ play: () => coreRef.current?.play(),
123
+ pause: () => coreRef.current?.pause(),
124
+ stop: () => coreRef.current?.stop(),
125
+ seekToFrame: (frame: number) => coreRef.current?.seekToFrame(frame),
126
+ seekToTime: (seconds: number) => coreRef.current?.seekToTime(seconds),
127
+ }), []);
128
+
129
+ // A Blob or ByteSource plays as it is; anything else is normalized to { uri } format
130
+ const directSource = isDirectSource(source) ? source : null;
131
+ const normalizedSource = useMemo((): SourceType | null => {
132
+ if (!source || isDirectSource(source)) return null;
133
+ if (typeof source === 'string') return { uri: source };
134
+ return source;
135
+ }, [source]);
136
+ const headersRef = useRef(normalizedSource?.headers);
137
+ headersRef.current = normalizedSource?.headers;
138
+
139
+ // Create core instance when canvas is available
140
+ useEffect(() => {
141
+ const canvas = canvasRef.current;
142
+ if (!canvas || coreRef.current) return;
143
+
144
+ coreRef.current = new AlphaVideoGpuCore(canvas, {
145
+ autoPlay,
146
+ loop,
147
+ playbackSpeed,
148
+ stream,
149
+ streamingPrefetchAhead,
150
+ memoryStrategy,
151
+ verbose,
152
+ frameEvents: enableFrameEvents,
153
+ storage: storage === 'disk' ? 'auto' : storage,
154
+ maxStorageBytes: maxStorageBytes && maxStorageBytes > 0 ? maxStorageBytes : undefined,
155
+ ZSTDDecoderClass: ZSTDDecoder,
156
+ onStateChange: state => callbacksRef.current.onStateChange?.({ nativeEvent: { state } }),
157
+ onLoad: event => callbacksRef.current.onLoad?.({ nativeEvent: event }),
158
+ onFrameUpdate: (frame, totalFrames) =>
159
+ callbacksRef.current.onFrameUpdate?.({ nativeEvent: { frame, totalFrames } }),
160
+ onFinished: () => callbacksRef.current.onFinished?.({ nativeEvent: {} }),
161
+ onError: error => {
162
+ const { code } = error as AlphaVideoGpuError;
163
+ callbacksRef.current.onError?.({ nativeEvent: code ? { error: error.message, code } : { error: error.message } });
164
+ },
165
+ });
166
+
167
+ return () => {
168
+ coreRef.current?.destroy();
169
+ coreRef.current = null;
170
+ loadedSourceRef.current = null;
171
+ };
172
+ // Only create/destroy on mount/unmount
173
+ // eslint-disable-next-line react-hooks/exhaustive-deps
174
+ }, []);
175
+
176
+ // What identifies "the same video". A presigned URL is re-signed every time the app
177
+ // lists its media, so the URL changes while still addressing the same object; keying
178
+ // the load on it would resetSource() and refetch on each refresh, visibly restarting
179
+ // a loop that should run until unmount. The cache key is the stable identity.
180
+ const sourceCacheKey = normalizedSource?.cacheKey || cacheKey;
181
+ const sourceIdentity = directSource || sourceCacheKey || normalizedSource?.uri || null;
182
+
183
+ // Load source when it changes
184
+ useEffect(() => {
185
+ const core = coreRef.current;
186
+ const toLoad = directSource || normalizedSource?.uri;
187
+ if (!core || !toLoad || !sourceIdentity || loadedSourceRef.current === sourceIdentity) return;
188
+
189
+ loadedSourceRef.current = sourceIdentity;
190
+ core.setSource(toLoad, { cacheKey: sourceCacheKey, headers: headersRef.current });
191
+
192
+ return () => {
193
+ loadedSourceRef.current = null;
194
+ core.resetSource();
195
+ };
196
+ // eslint-disable-next-line react-hooks/exhaustive-deps
197
+ }, [sourceIdentity]);
198
+
199
+ useEffect(() => {
200
+ if (coreRef.current) {
201
+ coreRef.current.verbose = verbose;
202
+ }
203
+ }, [verbose]);
204
+
205
+ useEffect(() => {
206
+ if (coreRef.current) {
207
+ coreRef.current.frameEvents = enableFrameEvents;
208
+ }
209
+ }, [enableFrameEvents]);
210
+
211
+ // Handle playback speed changes
212
+ useEffect(() => {
213
+ if (coreRef.current) {
214
+ coreRef.current.playbackSpeed = playbackSpeed;
215
+ }
216
+ }, [playbackSpeed]);
217
+
218
+ // Handle loop changes
219
+ useEffect(() => {
220
+ if (coreRef.current) {
221
+ coreRef.current.loop = loop;
222
+ }
223
+ }, [loop]);
224
+
225
+ // Handle paused prop changes
226
+ useEffect(() => {
227
+ coreRef.current?.setPaused(paused);
228
+ }, [paused]);
229
+
230
+ // Get canvas dimensions from style
231
+ const canvasStyle = useMemo(() => {
232
+ const flatStyle = StyleSheet.flatten(style) || {};
233
+ return {
234
+ width: (flatStyle as any).width || 300,
235
+ height: (flatStyle as any).height || 300,
236
+ };
237
+ }, [style]);
238
+
239
+ return (
240
+ <View testID={testID} style={[styles.container, style]}>
241
+ <canvas
242
+ ref={canvasRef}
243
+ width={canvasStyle.width}
244
+ height={canvasStyle.height}
245
+ style={{ width: '100%', height: '100%' }}
246
+ />
247
+ </View>
248
+ );
249
+ });
250
+
251
+ function isDirectSource(source: AlphaVideoGpuViewProps['source']): source is Blob | ByteSource {
252
+ if (!source || typeof source !== 'object') return false;
253
+ return (typeof Blob !== 'undefined' && source instanceof Blob) || typeof (source as ByteSource).read === 'function';
254
+ }
255
+
256
+ const styles = StyleSheet.create({
257
+ container: {
258
+ overflow: 'hidden',
259
+ },
260
+ });
261
+
262
+ AlphaVideoGpuView.displayName = 'AlphaVideoGpuView';
263
+
264
+ 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;
@@ -0,0 +1,95 @@
1
+ import type { ViewStyle } from 'react-native';
2
+ import type React from 'react';
3
+
4
+ // Re-export native component types
5
+ export * from './AlphaVideoGpuViewNativeComponent';
6
+
7
+ // Source type for the wrapper component (see SourceType for the URI forms, headers and dataSource)
8
+ export type AlphaVideoGpuSource = import('./AlphaVideoGpuViewNativeComponent').SourceType;
9
+
10
+ /**
11
+ * Where a URL's or data source's bytes are kept between plays, read back by range: 'disk' (the
12
+ * cache folder; the browser's storage on web), 'memory' (for the app's lifetime) or 'none'. 'auto',
13
+ * the default, is 'disk' for URLs and 'none' for a data source, whose caching is its own.
14
+ */
15
+ export type AlphaVideoGpuStorage = 'auto' | 'disk' | 'memory' | 'none';
16
+
17
+ /** Web only: reads the file by offset (an app's own fetcher or cache); the smallest is `{ size, read }`. */
18
+ export interface AlphaVideoGpuByteSource {
19
+ open?(signal?: AbortSignal): Promise<{ size: number; etag?: string; lastModified?: string }>;
20
+ readonly size?: number;
21
+ read(offset: number, length: number, signal?: AbortSignal): Promise<Uint8Array>;
22
+ close?(): void;
23
+ }
24
+
25
+ // Props for the main AlphaVideoGpuView wrapper component (index.js)
26
+ export interface AlphaVideoGpuViewProps {
27
+ /** Source object, or a bare URI string (legacy). On web also a Blob or a byte source */
28
+ source?: AlphaVideoGpuSource | string | Blob | AlphaVideoGpuByteSource | null;
29
+ /** Start playing automatically (default: true) */
30
+ autoPlay?: boolean;
31
+ /** Loop the animation (default: true) */
32
+ loop?: boolean;
33
+ /** Playback speed multiplier (default: 1.0) */
34
+ playbackSpeed?: number;
35
+ /** What the store keeps the source under, instead of its URL (same as `source.cacheKey`) */
36
+ cacheKey?: string;
37
+ /** Where a URL's or data source's bytes are kept between plays. Default: 'auto' */
38
+ storage?: AlphaVideoGpuStorage;
39
+ /** The store's cap in bytes, over every clip, least recently played out first. Default: 512 MB */
40
+ maxStorageBytes?: number;
41
+ /** Pause playback */
42
+ paused?: boolean;
43
+ /** View style */
44
+ style?: ViewStyle;
45
+ /** Locates the view in end-to-end tests */
46
+ testID?: string;
47
+ // Every event has this shape on Android, iOS and web
48
+ /** Callback when player state changes */
49
+ onStateChange?: (event: { nativeEvent: { state: string } }) => void;
50
+ /** Callback after a seek, and for every frame played with `enableFrameEvents` */
51
+ onFrameUpdate?: (event: { nativeEvent: { frame: number; totalFrames: number } }) => void;
52
+ /** Callback when playback finishes */
53
+ onFinished?: (event: { nativeEvent: {} }) => void;
54
+ /**
55
+ * Callback on error. `code` tells apart the errors an app may handle: on the web
56
+ * 'ASTC_UNSUPPORTED' or 'WEBGL_UNSUPPORTED', on iOS and Android 'NO_SOURCE_MODULE' (a data
57
+ * source without the range-source add-on)
58
+ */
59
+ onError?: (event: { nativeEvent: { error: string; code?: 'ASTC_UNSUPPORTED' | 'WEBGL_UNSUPPORTED' | 'NO_SOURCE_MODULE' | '' } }) => void;
60
+ /** Callback when loaded */
61
+ onLoad?: (event: { nativeEvent: { width: number; height: number; frameCount: number; frameRate: number; duration: number } }) => void;
62
+
63
+ // Streaming parameters
64
+ /** Frames decoded ahead of the shown one, all streaming keeps (in the GPU ring with 'vram', in RAM with 'ram') */
65
+ streamingPrefetchAhead?: number;
66
+ /** Where frames are kept: 'vram' (default) or 'ram' */
67
+ memoryStrategy?: 'vram' | 'ram';
68
+ /** Stream texture-array files instead of loading every frame. Default: true */
69
+ stream?: boolean;
70
+ /** Send onFrameUpdate for every frame played; seeks always send it. Default: false */
71
+ enableFrameEvents?: boolean;
72
+ /** Log info traces. Warnings and errors always log. Default: false */
73
+ verbose?: boolean;
74
+ }
75
+
76
+ /**
77
+ * Seeks show the frame (clamped to the clip) and keep playing, or stay paused. They are ignored
78
+ * until `onLoad`. With `stream`, a seek outside the buffered frames decodes the file again from
79
+ * the start (or onwards from the decoder, for a seek ahead of it).
80
+ */
81
+ export interface AlphaVideoGpuViewRef {
82
+ /** Plays from the shown frame, or from the start once a clip that doesn't loop has finished. */
83
+ play: () => void;
84
+ pause: () => void;
85
+ /** Pauses on the first frame. */
86
+ stop: () => void;
87
+ seekToFrame: (frame: number) => void;
88
+ /** Seeks to the frame showing at that time, in seconds. */
89
+ seekToTime: (seconds: number) => void;
90
+ }
91
+
92
+ // The actual component is defined in index.js, but we declare the type here
93
+ export declare const AlphaVideoGpuView: React.MemoExoticComponent<
94
+ React.ForwardRefExoticComponent<AlphaVideoGpuViewProps & React.RefAttributes<AlphaVideoGpuViewRef>>
95
+ >;