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.
- package/LICENSE +21 -0
- package/README.md +949 -2
- package/THIRD_PARTY_NOTICES.md +14 -0
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/cpp/CMakeLists.txt +41 -0
- package/android/src/main/cpp/alphavideogpu_jni.cpp +1168 -0
- package/android/src/main/cpp/astc_decomp.cpp +1550 -0
- package/android/src/main/cpp/astc_decomp.h +41 -0
- package/android/src/main/cpp/source_jni.cpp +242 -0
- package/android/src/main/cpp/source_jni.h +13 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuNative.java +43 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuReader.java +209 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuSource.java +328 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/AlphaVideoGpuView.java +2126 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/Fetcher.java +37 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/HttpFetcher.java +156 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/Ktx2Metadata.java +78 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/RangeSourceLink.java +54 -0
- package/android/src/main/java/dev/mediacache/alphavideo/gpu/ZstdSeekTable.java +120 -0
- package/android/src/rangesource/java/dev/mediacache/alphavideo/gpu/AppDataSource.java +50 -0
- package/ios/Sources/AlphaVideoGPU/AlphaVideoGpuSource.h +63 -0
- package/ios/Sources/AlphaVideoGPU/AlphaVideoGpuSource.mm +522 -0
- package/ios/Sources/AlphaVideoGPU/AlphaVideoGpuView.mm +2722 -0
- package/ios/Sources/AlphaVideoGPU/astc_decomp.cpp +1550 -0
- package/ios/Sources/AlphaVideoGPU/astc_decomp.h +41 -0
- package/ios/Sources/AlphaVideoGPU/include/AlphaVideoGPU.h +110 -0
- package/ios/Sources/AlphaVideoGPU/zstd_seek.h +250 -0
- package/package.json +77 -4
- package/react-native/android/build.gradle +92 -0
- package/react-native/android/src/main/AndroidManifest.xml +9 -0
- package/react-native/android/src/main/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuPackage.java +26 -0
- package/react-native/android/src/main/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuViewManager.java +187 -0
- package/react-native/android/src/main/java/dev/mediacache/alphavideo/gpu/reactnative/PlayerEventForwarder.java +107 -0
- package/react-native/android/src/newarch/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuViewManagerSpec.java +23 -0
- package/react-native/android/src/norangesource/java/dev/mediacache/alphavideo/gpu/AppDataSource.java +15 -0
- package/react-native/android/src/oldarch/java/dev/mediacache/alphavideo/gpu/reactnative/AlphaVideoGpuViewManagerSpec.java +56 -0
- package/react-native/ios/RNAlphaVideoGpuViewComponentView.mm +176 -0
- package/react-native/ios/RNAlphaVideoGpuViewManager.mm +175 -0
- package/react-native/src/AlphaVideoGpuView.js +2 -0
- package/react-native/src/AlphaVideoGpuView.web.tsx +264 -0
- package/react-native/src/AlphaVideoGpuViewNativeComponent.ts +128 -0
- package/react-native/src/AlphaVideoGpuViewNativeComponent.web.ts +2 -0
- package/react-native/src/index.d.ts +95 -0
- package/react-native/src/index.js +162 -0
- package/react-native-alpha-video-gpu.podspec +37 -0
- package/react-native.config.js +12 -0
- package/third_party/licenses/Apache-2.0.txt +208 -0
- package/third_party/range-source/LICENSE +21 -0
- package/third_party/range-source/README.md +10 -0
- package/third_party/range-source/include/range_source.h +395 -0
- package/third_party/range-source/include/range_source_download.h +309 -0
- package/third_party/zstd/LICENSE +30 -0
- package/third_party/zstd/zstd.c +38717 -0
- package/third_party/zstd/zstd.h +2450 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#ifndef _TCUASTCUTIL_HPP
|
|
2
|
+
#define _TCUASTCUTIL_HPP
|
|
3
|
+
/*-------------------------------------------------------------------------
|
|
4
|
+
* drawElements Quality Program Tester Core
|
|
5
|
+
* ----------------------------------------
|
|
6
|
+
*
|
|
7
|
+
* Copyright 2016 The Android Open Source Project
|
|
8
|
+
*
|
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
* you may not use this file except in compliance with the License.
|
|
11
|
+
* You may obtain a copy of the License at
|
|
12
|
+
*
|
|
13
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
*
|
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
* See the License for the specific language governing permissions and
|
|
19
|
+
* limitations under the License.
|
|
20
|
+
*
|
|
21
|
+
*//*!
|
|
22
|
+
* \file
|
|
23
|
+
* \brief ASTC Utilities.
|
|
24
|
+
*//*--------------------------------------------------------------------*/
|
|
25
|
+
|
|
26
|
+
#include <vector>
|
|
27
|
+
|
|
28
|
+
namespace basisu
|
|
29
|
+
{
|
|
30
|
+
namespace astc
|
|
31
|
+
{
|
|
32
|
+
|
|
33
|
+
// Unpacks a single ASTC block to pDst
|
|
34
|
+
// If isSRGB is true, the spec requires the decoder to scale the LDR 8-bit endpoints to 16-bit before interpolation slightly differently,
|
|
35
|
+
// which will lead to different outputs. So be sure to set it correctly (ideally it should match whatever the encoder did).
|
|
36
|
+
bool decompress(uint8_t* pDst, const uint8_t* data, bool isSRGB, int blockWidth, int blockHeight);
|
|
37
|
+
|
|
38
|
+
} // astc
|
|
39
|
+
} // basisu
|
|
40
|
+
|
|
41
|
+
#endif
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlphaVideoGPU.h
|
|
3
|
+
* Transparent animations for iOS from ASTC KTX2 texture arrays, drawn with Metal. The simulator
|
|
4
|
+
* decodes the ASTC in software.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#import <Metal/Metal.h>
|
|
8
|
+
#import <MetalKit/MetalKit.h>
|
|
9
|
+
#import <UIKit/UIKit.h>
|
|
10
|
+
|
|
11
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* onError's code for a data source without the range-source add-on, which reads them (the
|
|
15
|
+
* RangeSource SwiftPM product, the alpha-video-gpu pod's Source subspec, or for React Native
|
|
16
|
+
* react-native-range-source). Without it a URL is downloaded whole for each play, and storage,
|
|
17
|
+
* maxStorageBytes and cacheKey are ignored.
|
|
18
|
+
*/
|
|
19
|
+
extern NSString *const AlphaVideoGpuErrorNoSourceModule;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Plays a KTX2 animation with alpha. A new `source` loads on the next main-queue turn, so set the
|
|
23
|
+
* other properties in the same turn and they apply to it.
|
|
24
|
+
*
|
|
25
|
+
* It is an MTKView that draws on demand, so leave the inherited `paused`, `delegate` and
|
|
26
|
+
* `enableSetNeedsDisplay` alone; use `playbackPaused` to pause. Use it from the main thread;
|
|
27
|
+
* the blocks are called there too.
|
|
28
|
+
*/
|
|
29
|
+
@interface AlphaVideoGpuView : MTKView
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What to play, or nil: https:// or http:// (with range-source read by range as it plays, and
|
|
33
|
+
* kept as storage says; without it downloaded whole), file:// or an absolute path, asset://path
|
|
34
|
+
* inside the app bundle, or with dataSource any URL that data source reads.
|
|
35
|
+
*/
|
|
36
|
+
@property (nonatomic, copy, nullable) NSString *source;
|
|
37
|
+
/**
|
|
38
|
+
* What the store keeps the source under, instead of its URL, for URLs that change while the file
|
|
39
|
+
* doesn't (presigned URLs). A source with the same cacheKey as the one playing keeps playing.
|
|
40
|
+
*/
|
|
41
|
+
@property (nonatomic, copy, nullable) NSString *cacheKey;
|
|
42
|
+
/** Sent with every request for an http(s) source, and given to a data source. */
|
|
43
|
+
@property (nonatomic, copy, nullable) NSDictionary<NSString *, NSString *> *headers;
|
|
44
|
+
/**
|
|
45
|
+
* Reads the source through the data source registered under this name with range-source's
|
|
46
|
+
* RangeDataSources. Needs the range-source add-on.
|
|
47
|
+
*/
|
|
48
|
+
@property (nonatomic, copy, nullable) NSString *dataSource;
|
|
49
|
+
/**
|
|
50
|
+
* Where a URL's or data source's bytes are kept between plays: "disk" (Caches/alpha-video-gpu),
|
|
51
|
+
* "memory" (for the app's lifetime) or "none". "auto" (the default) is disk for URLs and none for
|
|
52
|
+
* a data source, whose caching is its own. The store is partial: it keeps what has been played,
|
|
53
|
+
* and a clip held whole plays again offline.
|
|
54
|
+
*/
|
|
55
|
+
@property (nonatomic, copy, null_resettable) NSString *storage;
|
|
56
|
+
/** The store's cap over every clip in it, least recently played out first; 0 or less for 512 MB. */
|
|
57
|
+
@property (nonatomic, assign) int64_t maxStorageBytes;
|
|
58
|
+
|
|
59
|
+
@property (nonatomic, assign) BOOL autoPlay; // Default YES
|
|
60
|
+
@property (nonatomic, assign) BOOL loop; // Default YES
|
|
61
|
+
@property (nonatomic, assign) CGFloat playbackSpeed; // Default 1.0
|
|
62
|
+
/** Pauses or resumes playback, keeping the loaded frames (for screen focus and blur). */
|
|
63
|
+
@property (nonatomic, assign) BOOL playbackPaused;
|
|
64
|
+
/** Calls onFrameUpdate for every frame shown. Default NO. */
|
|
65
|
+
@property (nonatomic, assign) BOOL enableFrameEvents;
|
|
66
|
+
|
|
67
|
+
/** Streams texture-array files through a ring of GPU slots instead of loading every frame. Default YES */
|
|
68
|
+
@property (nonatomic, assign) BOOL stream;
|
|
69
|
+
@property (nonatomic, assign) NSInteger streamingPrefetchAhead; // Frames decoded ahead of the shown one, all streaming keeps: the GPU ring (vram) or RAM (ram). 1 to 100, default 8
|
|
70
|
+
/** "vram" (the default: frames on the GPU) or "ram" (decoded frames in RAM). Any other value means "vram". */
|
|
71
|
+
@property (nonatomic, copy) NSString *memoryStrategy;
|
|
72
|
+
/** Logs info traces. Warnings and errors always log. Default NO. */
|
|
73
|
+
@property (nonatomic, assign) BOOL verbose;
|
|
74
|
+
|
|
75
|
+
@property (nonatomic, readonly) NSInteger currentFrame;
|
|
76
|
+
@property (nonatomic, readonly) NSInteger totalFrames;
|
|
77
|
+
@property (nonatomic, readonly) CGFloat frameRate;
|
|
78
|
+
@property (nonatomic, readonly) BOOL isPlaying;
|
|
79
|
+
@property (nonatomic, readonly) NSInteger frameWidth;
|
|
80
|
+
@property (nonatomic, readonly) NSInteger frameHeight;
|
|
81
|
+
|
|
82
|
+
/** The animation is loaded: frame size in pixels, frame count and frames per second. */
|
|
83
|
+
@property (nonatomic, copy, nullable) void (^onLoad)(NSInteger width, NSInteger height, NSInteger frameCount, CGFloat frameRate);
|
|
84
|
+
/** The shown frame changed, after seekToFrame:, and every frame when enableFrameEvents is on. */
|
|
85
|
+
@property (nonatomic, copy, nullable) void (^onFrameUpdate)(NSInteger frame, NSInteger totalFrames);
|
|
86
|
+
/** "loading", "ready", "playing", "paused" or "stopped". */
|
|
87
|
+
@property (nonatomic, copy, nullable) void (^onStateChange)(NSString *state);
|
|
88
|
+
/** Playback reached the last frame with loop off. */
|
|
89
|
+
@property (nonatomic, copy, nullable) void (^onFinished)(void);
|
|
90
|
+
/** A load or playback error; `code` is set for those an app may handle, such as AlphaVideoGpuErrorNoSourceModule. */
|
|
91
|
+
@property (nonatomic, copy, nullable) void (^onError)(NSString *error, NSString *_Nullable code);
|
|
92
|
+
|
|
93
|
+
/** Plays from the shown frame, or from the start once a clip that doesn't loop has finished. */
|
|
94
|
+
- (void)play;
|
|
95
|
+
- (void)pause;
|
|
96
|
+
/** Pauses on the first frame. */
|
|
97
|
+
- (void)stop;
|
|
98
|
+
/**
|
|
99
|
+
* Shows that frame (clamped to the clip) and keeps playing or stays paused. Ignored until onLoad.
|
|
100
|
+
* With stream, a seek outside the buffered frames decodes the file again.
|
|
101
|
+
*/
|
|
102
|
+
- (void)seekToFrame:(NSInteger)frame;
|
|
103
|
+
/** Seeks to the frame showing at that time, in seconds. */
|
|
104
|
+
- (void)seekToTime:(NSTimeInterval)seconds;
|
|
105
|
+
/** Stops playback and frees the frames. Removing the view from its superview does the same. */
|
|
106
|
+
- (void)cleanup;
|
|
107
|
+
|
|
108
|
+
@end
|
|
109
|
+
|
|
110
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming zstd for a KTX2 level, with seeking. texenc can write the level as several zstd
|
|
3
|
+
* frames followed by a seek table in zstd's seekable format (contrib/seekable_format): a
|
|
4
|
+
* skippable frame listing each frame's compressed and decompressed size, ending with a 9-byte
|
|
5
|
+
* footer. The decoder can start at any frame, so a seek decodes at most one frame's worth before
|
|
6
|
+
* its target. A level without a table is a single stream, which decodes only from its start.
|
|
7
|
+
*
|
|
8
|
+
* Header-only C, so the player includes it and host tests (ios/Tests) compile it as is.
|
|
9
|
+
*/
|
|
10
|
+
#ifndef AVG_ZSTD_SEEK_H
|
|
11
|
+
#define AVG_ZSTD_SEEK_H
|
|
12
|
+
|
|
13
|
+
#include <stdbool.h>
|
|
14
|
+
#include <stdint.h>
|
|
15
|
+
#include <stdlib.h>
|
|
16
|
+
#include <string.h>
|
|
17
|
+
#include "zstd/zstd.h"
|
|
18
|
+
|
|
19
|
+
typedef struct {
|
|
20
|
+
/** Number of frames; 0 for a level without a table. */
|
|
21
|
+
size_t count;
|
|
22
|
+
/** Where each frame starts in the level data, and where its output starts once decompressed. */
|
|
23
|
+
uint64_t *compressedOffsets;
|
|
24
|
+
uint64_t *decompressedOffsets;
|
|
25
|
+
} AVGZstdSeekTable;
|
|
26
|
+
|
|
27
|
+
/** A frame start: where the decoder can begin. */
|
|
28
|
+
typedef struct {
|
|
29
|
+
uint64_t compressedOffset;
|
|
30
|
+
uint64_t decompressedOffset;
|
|
31
|
+
} AVGZstdSeekPoint;
|
|
32
|
+
|
|
33
|
+
static inline uint32_t avg_read_u32le(const uint8_t *p) {
|
|
34
|
+
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static inline void avg_zstd_seek_table_free(AVGZstdSeekTable *table) {
|
|
38
|
+
free(table->compressedOffsets);
|
|
39
|
+
free(table->decompressedOffsets);
|
|
40
|
+
memset(table, 0, sizeof(*table));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A guess at the bytes at the level's end that hold its table, for reading it over the network. */
|
|
44
|
+
#define AVG_ZSTD_SEEK_TAIL_GUESS (64 * 1024)
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* How many bytes at the level's end the table takes, read from its footer in the last bytes of
|
|
48
|
+
* tail; 0 when the footer isn't a seek table's.
|
|
49
|
+
*/
|
|
50
|
+
static inline uint64_t avg_zstd_seek_table_tail_size(const uint8_t *tail, size_t tailLength) {
|
|
51
|
+
const uint32_t footerMagic = 0x8F92EAB1;
|
|
52
|
+
const size_t footerSize = 9, skippableHeaderSize = 8;
|
|
53
|
+
if (tailLength < footerSize || avg_read_u32le(tail + tailLength - 4) != footerMagic) return 0;
|
|
54
|
+
uint8_t descriptor = tail[tailLength - 5];
|
|
55
|
+
if (descriptor & 0x7C) return 0; // reserved bits
|
|
56
|
+
uint64_t count = avg_read_u32le(tail + tailLength - footerSize);
|
|
57
|
+
uint64_t entrySize = (descriptor & 0x80) ? 12 : 8; // with checksums
|
|
58
|
+
return skippableHeaderSize + count * entrySize + footerSize;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Reads the table from tail, the last tailLength bytes of a level of levelLength bytes, into
|
|
63
|
+
* table (freed with avg_zstd_seek_table_free). False, with an empty table, when there is none,
|
|
64
|
+
* tail doesn't hold all of it (avg_zstd_seek_table_tail_size says how much does), or it doesn't
|
|
65
|
+
* describe exactly the frames before it and decompressedLength.
|
|
66
|
+
*/
|
|
67
|
+
static inline bool avg_zstd_seek_table_parse_tail(const uint8_t *tail, size_t tailLength, uint64_t levelLength,
|
|
68
|
+
uint64_t decompressedLength, AVGZstdSeekTable *table) {
|
|
69
|
+
const uint32_t tableMagic = 0x184D2A5E;
|
|
70
|
+
const size_t footerSize = 9, skippableHeaderSize = 8;
|
|
71
|
+
memset(table, 0, sizeof(*table));
|
|
72
|
+
if (tailLength > levelLength || tailLength < skippableHeaderSize + footerSize) return false;
|
|
73
|
+
uint64_t tailSize = avg_zstd_seek_table_tail_size(tail, tailLength);
|
|
74
|
+
if (tailSize == 0 || tailSize > tailLength) return false;
|
|
75
|
+
uint8_t descriptor = tail[tailLength - 5];
|
|
76
|
+
uint64_t count = avg_read_u32le(tail + tailLength - footerSize);
|
|
77
|
+
uint64_t entrySize = (descriptor & 0x80) ? 12 : 8;
|
|
78
|
+
uint64_t tableSize = tailSize - skippableHeaderSize;
|
|
79
|
+
if (count == 0) return false;
|
|
80
|
+
const uint8_t *tableBytes = tail + tailLength - tailSize;
|
|
81
|
+
uint64_t tableStart = levelLength - tailSize;
|
|
82
|
+
if (avg_read_u32le(tableBytes) != tableMagic) return false;
|
|
83
|
+
if (avg_read_u32le(tableBytes + 4) != tableSize) return false;
|
|
84
|
+
|
|
85
|
+
uint64_t *compressedOffsets = (uint64_t *)malloc(count * sizeof(uint64_t));
|
|
86
|
+
uint64_t *decompressedOffsets = (uint64_t *)malloc(count * sizeof(uint64_t));
|
|
87
|
+
if (!compressedOffsets || !decompressedOffsets) {
|
|
88
|
+
free(compressedOffsets);
|
|
89
|
+
free(decompressedOffsets);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
uint64_t compressed = 0, decompressed = 0;
|
|
93
|
+
for (size_t i = 0; i < count; i++) {
|
|
94
|
+
const uint8_t *entry = tableBytes + skippableHeaderSize + i * entrySize;
|
|
95
|
+
compressedOffsets[i] = compressed;
|
|
96
|
+
decompressedOffsets[i] = decompressed;
|
|
97
|
+
compressed += avg_read_u32le(entry);
|
|
98
|
+
decompressed += avg_read_u32le(entry + 4);
|
|
99
|
+
}
|
|
100
|
+
if (compressed != tableStart || decompressed != decompressedLength) {
|
|
101
|
+
free(compressedOffsets);
|
|
102
|
+
free(decompressedOffsets);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
table->count = (size_t)count;
|
|
106
|
+
table->compressedOffsets = compressedOffsets;
|
|
107
|
+
table->decompressedOffsets = decompressedOffsets;
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** avg_zstd_seek_table_parse_tail with the whole level in memory. */
|
|
112
|
+
static inline bool avg_zstd_seek_table_parse(const uint8_t *level, size_t length, uint64_t decompressedLength,
|
|
113
|
+
AVGZstdSeekTable *table) {
|
|
114
|
+
return avg_zstd_seek_table_parse_tail(level, length, length, decompressedLength, table);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The last frame starting at or before that decompressed offset; the level start without a table. */
|
|
118
|
+
static inline AVGZstdSeekPoint avg_zstd_seek_point_before(const AVGZstdSeekTable *table, uint64_t decompressedOffset) {
|
|
119
|
+
AVGZstdSeekPoint start = {0, 0};
|
|
120
|
+
if (!table || table->count == 0) return start;
|
|
121
|
+
size_t low = 0, high = table->count - 1;
|
|
122
|
+
while (low < high) {
|
|
123
|
+
size_t mid = (low + high + 1) / 2;
|
|
124
|
+
if (table->decompressedOffsets[mid] <= decompressedOffset) low = mid;
|
|
125
|
+
else high = mid - 1;
|
|
126
|
+
}
|
|
127
|
+
AVGZstdSeekPoint point = {table->compressedOffsets[low], table->decompressedOffsets[low]};
|
|
128
|
+
return point;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* For a target at or ahead of the decoder: whether a decoder that has produced `decoded` bytes
|
|
133
|
+
* reaches it sooner by carrying on than by starting at point, the target's seek point.
|
|
134
|
+
*/
|
|
135
|
+
static inline bool avg_zstd_carry_on(uint64_t decoded, AVGZstdSeekPoint point) {
|
|
136
|
+
return decoded >= point.decompressedOffset;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Copies up to length bytes of the level from offset into buffer without waiting for them:
|
|
141
|
+
* returns how many, 0 at the end of the file, AVG_ZSTD_READ_PENDING while they're on their way,
|
|
142
|
+
* or -1 if they can't be read.
|
|
143
|
+
*/
|
|
144
|
+
typedef int64_t (*AVGZstdReadFunction)(void *context, uint64_t offset, uint8_t *buffer, size_t length);
|
|
145
|
+
#define AVG_ZSTD_READ_PENDING (-2)
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A zstd stream over level data: held in memory (a mapping of the file, in the player), or with
|
|
149
|
+
* read set, read through it into chunk (a URL's bytes as they arrive).
|
|
150
|
+
*/
|
|
151
|
+
typedef struct {
|
|
152
|
+
ZSTD_DStream *dstream;
|
|
153
|
+
// The level's bytes in memory; NULL with read
|
|
154
|
+
const uint8_t *data;
|
|
155
|
+
size_t size;
|
|
156
|
+
size_t pos;
|
|
157
|
+
// The last call filled the output mid-frame: zstd may hold more even with no input left
|
|
158
|
+
bool outputPending;
|
|
159
|
+
|
|
160
|
+
AVGZstdReadFunction read;
|
|
161
|
+
void *readContext;
|
|
162
|
+
// The caller's buffer of chunkCapacity bytes, holding the level's [chunkStart, chunkStart + chunkLength)
|
|
163
|
+
uint8_t *chunk;
|
|
164
|
+
size_t chunkCapacity;
|
|
165
|
+
size_t chunkStart;
|
|
166
|
+
size_t chunkLength;
|
|
167
|
+
// The last call stopped at input that hasn't arrived yet: call again later
|
|
168
|
+
bool inputPending;
|
|
169
|
+
} AVGZstdStream;
|
|
170
|
+
|
|
171
|
+
/** Starts decoding again at a frame start in the data (0 is the level's start). */
|
|
172
|
+
static inline void avg_zstd_stream_restart(AVGZstdStream *stream, uint64_t compressedOffset) {
|
|
173
|
+
ZSTD_initDStream(stream->dstream);
|
|
174
|
+
stream->pos = compressedOffset < stream->size ? (size_t)compressedOffset : stream->size;
|
|
175
|
+
stream->outputPending = false;
|
|
176
|
+
stream->inputPending = false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* For a stream with read: makes chunk hold the byte at pos. 1 when it does, 0 while it's on its
|
|
181
|
+
* way, -1 with *error set when it can't be read.
|
|
182
|
+
*/
|
|
183
|
+
static inline int avg_zstd_stream_fill(AVGZstdStream *stream, const char **error) {
|
|
184
|
+
if (stream->pos >= stream->chunkStart && stream->pos < stream->chunkStart + stream->chunkLength) return 1;
|
|
185
|
+
size_t left = stream->size - stream->pos;
|
|
186
|
+
size_t want = left < stream->chunkCapacity ? left : stream->chunkCapacity;
|
|
187
|
+
int64_t n = stream->read(stream->readContext, stream->pos, stream->chunk, want);
|
|
188
|
+
if (n == AVG_ZSTD_READ_PENDING) return 0;
|
|
189
|
+
if (n <= 0) {
|
|
190
|
+
if (error) *error = n == 0 ? "the file ends inside the level" : "reading failed";
|
|
191
|
+
return -1;
|
|
192
|
+
}
|
|
193
|
+
stream->chunkStart = stream->pos;
|
|
194
|
+
stream->chunkLength = (size_t)n;
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** True once every byte of output is out: the input is used up and nothing is left to flush. */
|
|
199
|
+
static inline bool avg_zstd_stream_ended(const AVGZstdStream *stream) {
|
|
200
|
+
return stream->pos >= stream->size && !stream->outputPending;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Decompresses up to capacity bytes into out and returns how many; carries on across frame
|
|
205
|
+
* ends (several frames, and the skippable table, follow each other). Returns 0 once the stream
|
|
206
|
+
* has ended, and sets *error on corrupt data or a failed read, which end it. With read, it returns
|
|
207
|
+
* early with inputPending set when the next input hasn't arrived.
|
|
208
|
+
*/
|
|
209
|
+
static inline size_t avg_zstd_stream_read(AVGZstdStream *stream, void *out, size_t capacity, const char **error) {
|
|
210
|
+
size_t written = 0;
|
|
211
|
+
stream->inputPending = false;
|
|
212
|
+
while (written < capacity && !avg_zstd_stream_ended(stream)) {
|
|
213
|
+
// With read and no input to hand, the call below just flushes what zstd holds
|
|
214
|
+
ZSTD_inBuffer input = {NULL, 0, 0};
|
|
215
|
+
size_t inputBase = stream->read ? stream->pos : 0;
|
|
216
|
+
if (!stream->read) {
|
|
217
|
+
input = (ZSTD_inBuffer){stream->data, stream->size, stream->pos};
|
|
218
|
+
} else if (stream->pos < stream->size) {
|
|
219
|
+
int filled = avg_zstd_stream_fill(stream, error);
|
|
220
|
+
if (filled < 0) {
|
|
221
|
+
stream->pos = stream->size;
|
|
222
|
+
stream->outputPending = false;
|
|
223
|
+
return written;
|
|
224
|
+
}
|
|
225
|
+
if (filled == 0 && !stream->outputPending) {
|
|
226
|
+
stream->inputPending = true;
|
|
227
|
+
return written;
|
|
228
|
+
}
|
|
229
|
+
if (filled == 1) {
|
|
230
|
+
inputBase = stream->chunkStart;
|
|
231
|
+
input = (ZSTD_inBuffer){stream->chunk, stream->chunkLength, stream->pos - stream->chunkStart};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
ZSTD_outBuffer output = {(uint8_t *)out + written, capacity - written, 0};
|
|
235
|
+
size_t result = ZSTD_decompressStream(stream->dstream, &output, &input);
|
|
236
|
+
if (ZSTD_isError(result)) {
|
|
237
|
+
if (error) *error = ZSTD_getErrorName(result);
|
|
238
|
+
stream->pos = stream->size;
|
|
239
|
+
stream->outputPending = false;
|
|
240
|
+
return written;
|
|
241
|
+
}
|
|
242
|
+
stream->pos = inputBase + input.pos;
|
|
243
|
+
written += output.pos;
|
|
244
|
+
// 0 means the frame is done and flushed; calling again would start reading a next one
|
|
245
|
+
stream->outputPending = output.pos == output.size && result != 0;
|
|
246
|
+
}
|
|
247
|
+
return written;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#endif
|
package/package.json
CHANGED
|
@@ -1,6 +1,79 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-alpha-video-gpu",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
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
|
+
"main": "react-native/src/index.js",
|
|
6
|
+
"types": "react-native/src/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"react-native",
|
|
9
|
+
"ios/Sources",
|
|
10
|
+
"android/src",
|
|
11
|
+
"third_party",
|
|
12
|
+
"react-native.config.js",
|
|
13
|
+
"react-native-alpha-video-gpu.podspec",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"THIRD_PARTY_NOTICES.md",
|
|
17
|
+
"!android/src/test",
|
|
18
|
+
"!react-native/android/build",
|
|
19
|
+
"!react-native/android/.gradle",
|
|
20
|
+
"!react-native/android/.cxx",
|
|
21
|
+
"!.DS_Store"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"publish:android:local": "scripts/publish-android.sh local"
|
|
25
|
+
},
|
|
26
|
+
"codegenConfig": {
|
|
27
|
+
"name": "RNAlphaVideoGpuSpec",
|
|
28
|
+
"type": "components",
|
|
29
|
+
"jsSrcsDir": "react-native/src",
|
|
30
|
+
"android": {
|
|
31
|
+
"javaPackageName": "dev.mediacache.alphavideo.gpu.reactnative"
|
|
32
|
+
},
|
|
33
|
+
"ios": {
|
|
34
|
+
"componentProvider": {
|
|
35
|
+
"AlphaVideoGpuView": "RNAlphaVideoGpuViewComponentView"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/MediaCacheDev/alpha-video-gpu"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/MediaCacheDev/alpha-video-gpu#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/MediaCacheDev/alpha-video-gpu/issues"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"react-native",
|
|
49
|
+
"react-native-web",
|
|
50
|
+
"react-native-component",
|
|
51
|
+
"fabric",
|
|
52
|
+
"alpha",
|
|
53
|
+
"alpha-video",
|
|
54
|
+
"transparent-video",
|
|
55
|
+
"transparent-animation",
|
|
56
|
+
"transparency",
|
|
57
|
+
"animation",
|
|
58
|
+
"video-player",
|
|
59
|
+
"ktx2",
|
|
60
|
+
"astc",
|
|
61
|
+
"texture-compression",
|
|
62
|
+
"gpu",
|
|
63
|
+
"metal",
|
|
64
|
+
"opengl-es",
|
|
65
|
+
"webgl",
|
|
66
|
+
"ios",
|
|
67
|
+
"android",
|
|
68
|
+
"web"
|
|
69
|
+
],
|
|
70
|
+
"author": "Daniel Kasprowicz",
|
|
71
|
+
"license": "MIT",
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"alpha-video-gpu-web": "^1.0.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"react": ">=17.0.0",
|
|
77
|
+
"react-native": ">=0.71.0"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// The React Native package's Android module. It compiles the standalone library's sources
|
|
2
|
+
// (../../android) together with the React Native glue, because autolinking adds only this one
|
|
3
|
+
// Gradle project to the app.
|
|
4
|
+
|
|
5
|
+
def isNewArchitectureEnabled() {
|
|
6
|
+
// Set by the app's gradle.properties, and always "true" from React Native 0.82
|
|
7
|
+
return project.findProperty('newArchEnabled')?.toString() == 'true'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
apply plugin: 'com.android.library'
|
|
11
|
+
if (isNewArchitectureEnabled()) {
|
|
12
|
+
apply plugin: 'com.facebook.react'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def safeExtGet(name, fallback) {
|
|
16
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : fallback
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def coreDir = file('../../android')
|
|
20
|
+
def packageRoot = file('../..')
|
|
21
|
+
|
|
22
|
+
// range-source, the add-on the player reads URLs and data sources through, is the app's own
|
|
23
|
+
// dependency, react-native-range-source, which autolinking adds; the player finds it at run time
|
|
24
|
+
// (RangeSourceLink) and plays URLs without it.
|
|
25
|
+
def rangeSourceProject = rootProject.findProject(':react-native-range-source')
|
|
26
|
+
logger.lifecycle("react-native-alpha-video-gpu: range-source ${rangeSourceProject ? 'from react-native-range-source' : 'not installed (URLs are downloaded whole; npm i react-native-range-source for range streaming and storage)'}")
|
|
27
|
+
|
|
28
|
+
if (isNewArchitectureEnabled()) {
|
|
29
|
+
// The codegen tasks look for package.json and the spec next to this folder; both are two
|
|
30
|
+
// levels up, in the package root.
|
|
31
|
+
tasks.named('generateCodegenSchemaFromJavaScript').configure {
|
|
32
|
+
jsRootDir.set(file("${packageRoot}/react-native/src"))
|
|
33
|
+
jsInputFiles.set(fileTree("${packageRoot}/react-native/src") { include '**/*.ts', '**/*.tsx', '**/*.js' })
|
|
34
|
+
}
|
|
35
|
+
tasks.named('generateCodegenArtifactsFromSchema').configure {
|
|
36
|
+
packageJsonFile.set(file("${packageRoot}/package.json"))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
android {
|
|
41
|
+
namespace 'dev.mediacache.alphavideo.gpu.reactnative'
|
|
42
|
+
compileSdk safeExtGet('compileSdkVersion', 36)
|
|
43
|
+
ndkVersion safeExtGet('ndkVersion', '27.1.12297006')
|
|
44
|
+
|
|
45
|
+
defaultConfig {
|
|
46
|
+
minSdk safeExtGet('minSdkVersion', 24)
|
|
47
|
+
consumerProguardFiles "${coreDir}/consumer-rules.pro"
|
|
48
|
+
|
|
49
|
+
externalNativeBuild {
|
|
50
|
+
cmake {
|
|
51
|
+
cppFlags '-std=c++17 -fexceptions'
|
|
52
|
+
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
|
53
|
+
arguments '-DANDROID_STL=c++_shared'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
externalNativeBuild {
|
|
59
|
+
cmake {
|
|
60
|
+
path "${coreDir}/src/main/cpp/CMakeLists.txt"
|
|
61
|
+
version '3.22.1'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
compileOptions {
|
|
66
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
67
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
packagingOptions {
|
|
71
|
+
pickFirst 'lib/*/libc++_shared.so'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
sourceSets {
|
|
75
|
+
main {
|
|
76
|
+
java.srcDirs = [
|
|
77
|
+
'src/main/java',
|
|
78
|
+
"${coreDir}/src/main/java",
|
|
79
|
+
isNewArchitectureEnabled() ? 'src/newarch/java' : 'src/oldarch/java',
|
|
80
|
+
// AppDataSource, against range-source's classes, or a stub without them
|
|
81
|
+
rangeSourceProject ? "${coreDir}/src/rangesource/java" : 'src/norangesource/java',
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
dependencies {
|
|
88
|
+
implementation 'com.facebook.react:react-android'
|
|
89
|
+
if (rangeSourceProject) {
|
|
90
|
+
compileOnly rangeSourceProject
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
|
|
4
|
+
<!-- OpenGL ES 3.0, for texture arrays and ETC2/ASTC texture compression -->
|
|
5
|
+
<uses-feature
|
|
6
|
+
android:glEsVersion="0x00030000"
|
|
7
|
+
android:required="true" />
|
|
8
|
+
|
|
9
|
+
</manifest>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package dev.mediacache.alphavideo.gpu.reactnative;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.Collections;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
|
|
13
|
+
public class AlphaVideoGpuPackage implements ReactPackage {
|
|
14
|
+
|
|
15
|
+
@NonNull
|
|
16
|
+
@Override
|
|
17
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
18
|
+
return Collections.<ViewManager>singletonList(new AlphaVideoGpuViewManager());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@NonNull
|
|
22
|
+
@Override
|
|
23
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
24
|
+
return Collections.emptyList();
|
|
25
|
+
}
|
|
26
|
+
}
|