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,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,242 @@
1
+ // Readers over a Java AlphaVideoGpuDataSource (AlphaVideoGpuReader and its Upstream), and the
2
+ // check of the range-source add-on's table (RangeSourceLink)
3
+
4
+ #include "source_jni.h"
5
+
6
+ #include <android/log.h>
7
+ #include <pthread.h>
8
+
9
+ #include <algorithm>
10
+ #include <cstddef>
11
+ #include <string>
12
+ #include <vector>
13
+
14
+ #define LOG_TAG "AlphaVideoGpu"
15
+ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
16
+
17
+ namespace {
18
+
19
+ pthread_key_t detachKey;
20
+ pthread_once_t detachKeyOnce = PTHREAD_ONCE_INIT;
21
+
22
+ void detachThread(void* vm) {
23
+ static_cast<JavaVM*>(vm)->DetachCurrentThread();
24
+ }
25
+
26
+ // This thread's env. A native thread is attached until it exits, as a reader's fetch thread calls
27
+ // into Java for as long as it runs.
28
+ JNIEnv* threadEnv(JavaVM* vm) {
29
+ JNIEnv* env = nullptr;
30
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_OK) return env;
31
+ if (vm->AttachCurrentThread(&env, nullptr) != JNI_OK) return nullptr;
32
+ pthread_once(&detachKeyOnce, [] { pthread_key_create(&detachKey, detachThread); });
33
+ pthread_setspecific(detachKey, vm);
34
+ return env;
35
+ }
36
+
37
+ // Clears a pending Java exception; true if there was one.
38
+ bool clearException(JNIEnv* env) {
39
+ if (!env->ExceptionCheck()) return false;
40
+ env->ExceptionDescribe();
41
+ env->ExceptionClear();
42
+ return true;
43
+ }
44
+
45
+ std::string stringFromJava(JNIEnv* env, jstring value) {
46
+ if (!value) return {};
47
+ const char* chars = env->GetStringUTFChars(value, nullptr);
48
+ std::string result(chars);
49
+ env->ReleaseStringUTFChars(value, chars);
50
+ return result;
51
+ }
52
+
53
+ class JavaUpstream : public avg_rs::Upstream {
54
+ public:
55
+ static std::unique_ptr<JavaUpstream> create(JNIEnv* env, jobject upstream) {
56
+ auto result = std::unique_ptr<JavaUpstream>(new JavaUpstream());
57
+ env->GetJavaVM(&result->vm_);
58
+ jclass cls = env->GetObjectClass(upstream);
59
+ result->open_ = env->GetMethodID(cls, "open", "(J)J");
60
+ result->read_ = env->GetMethodID(cls, "read", "([BI)I");
61
+ result->close_ = env->GetMethodID(cls, "close", "()V");
62
+ result->cancel_ = env->GetMethodID(cls, "cancel", "()V");
63
+ result->etag_ = env->GetFieldID(cls, "etag", "Ljava/lang/String;");
64
+ result->lastModified_ = env->GetFieldID(cls, "lastModified", "Ljava/lang/String;");
65
+ result->error_ = env->GetFieldID(cls, "error", "Ljava/lang/String;");
66
+ env->DeleteLocalRef(cls);
67
+ jbyteArray buffer = env->NewByteArray(kBufferBytes);
68
+ if (clearException(env) || !buffer || !result->open_ || !result->read_ || !result->close_ ||
69
+ !result->cancel_ || !result->etag_ || !result->lastModified_ || !result->error_) {
70
+ return nullptr;
71
+ }
72
+ result->object_ = env->NewGlobalRef(upstream);
73
+ result->buffer_ = static_cast<jbyteArray>(env->NewGlobalRef(buffer));
74
+ env->DeleteLocalRef(buffer);
75
+ return result;
76
+ }
77
+
78
+ ~JavaUpstream() override {
79
+ JNIEnv* env = threadEnv(vm_);
80
+ if (!env) return;
81
+ if (object_) env->DeleteGlobalRef(object_);
82
+ if (buffer_) env->DeleteGlobalRef(buffer_);
83
+ }
84
+
85
+ bool open(int64_t position, avg_rs::SourceInfo& info, std::string& error) override {
86
+ JNIEnv* env = threadEnv(vm_);
87
+ if (!env) {
88
+ error = "Cannot attach the reader's thread to Java";
89
+ return false;
90
+ }
91
+ jlong size = env->CallLongMethod(object_, open_, static_cast<jlong>(position));
92
+ if (clearException(env)) {
93
+ error = "The data source threw";
94
+ return false;
95
+ }
96
+ if (size == -2) {
97
+ error = stringField(env, error_);
98
+ return false;
99
+ }
100
+ info.size = size >= 0 ? size : -1;
101
+ info.etag = stringField(env, etag_);
102
+ info.lastModified = stringField(env, lastModified_);
103
+ return true;
104
+ }
105
+
106
+ int64_t read(uint8_t* out, int64_t size, std::string& error) override {
107
+ JNIEnv* env = threadEnv(vm_);
108
+ if (!env) {
109
+ error = "Cannot attach the reader's thread to Java";
110
+ return -1;
111
+ }
112
+ jint n = env->CallIntMethod(object_, read_, buffer_, static_cast<jint>(std::min<int64_t>(size, kBufferBytes)));
113
+ if (clearException(env)) {
114
+ error = "The data source threw";
115
+ return -1;
116
+ }
117
+ if (n < 0) {
118
+ error = stringField(env, error_);
119
+ return -1;
120
+ }
121
+ if (n > 0) env->GetByteArrayRegion(buffer_, 0, n, reinterpret_cast<jbyte*>(out));
122
+ return n;
123
+ }
124
+
125
+ void close() override { call(close_); }
126
+ void cancel() override { call(cancel_); }
127
+
128
+ private:
129
+ static constexpr jint kBufferBytes = 64 * 1024;
130
+
131
+ JavaUpstream() = default;
132
+
133
+ void call(jmethodID method) {
134
+ JNIEnv* env = threadEnv(vm_);
135
+ if (!env) return;
136
+ env->CallVoidMethod(object_, method);
137
+ clearException(env);
138
+ }
139
+
140
+ std::string stringField(JNIEnv* env, jfieldID field) {
141
+ auto value = static_cast<jstring>(env->GetObjectField(object_, field));
142
+ std::string result = stringFromJava(env, value);
143
+ if (value) env->DeleteLocalRef(value);
144
+ return result;
145
+ }
146
+
147
+ JavaVM* vm_ = nullptr;
148
+ jobject object_ = nullptr;
149
+ jbyteArray buffer_ = nullptr;
150
+ jmethodID open_ = nullptr, read_ = nullptr, close_ = nullptr, cancel_ = nullptr;
151
+ jfieldID etag_ = nullptr, lastModified_ = nullptr, error_ = nullptr;
152
+ };
153
+
154
+ // An AlphaVideoGpuReader reference: a shared_ptr of its own, so each holder frees only its own.
155
+ using ReaderRef = std::shared_ptr<avg_rs::Reader>;
156
+
157
+ const ReaderRef& readerOf(jlong reference) {
158
+ return *reinterpret_cast<ReaderRef*>(reference);
159
+ }
160
+
161
+ } // namespace
162
+
163
+ std::shared_ptr<avg_rs::Reader> readerFromReference(jlong reference) {
164
+ return reference != 0 ? readerOf(reference) : nullptr;
165
+ }
166
+
167
+ extern "C" {
168
+
169
+ JNIEXPORT jlong JNICALL
170
+ Java_dev_mediacache_alphavideo_gpu_RangeSourceLink_nativeUsablePlugin(JNIEnv*, jclass, jlong address) {
171
+ const auto* plugin = reinterpret_cast<const RangeSourcePlugin*>(address);
172
+ if (!plugin) return 0;
173
+ if (plugin->abi != RANGE_SOURCE_ABI) {
174
+ LOGE("Not using range-source: its ABI is %d, and this player needs %d", plugin->abi, RANGE_SOURCE_ABI);
175
+ return 0;
176
+ }
177
+ if (plugin->structSize < offsetof(RangeSourcePlugin, releaseReader) + sizeof(plugin->releaseReader)) {
178
+ LOGE("Not using range-source: it is older than this player");
179
+ return 0;
180
+ }
181
+ return reinterpret_cast<jlong>(plugin);
182
+ }
183
+
184
+ JNIEXPORT jlong JNICALL
185
+ Java_dev_mediacache_alphavideo_gpu_AlphaVideoGpuReader_nativeOpen(
186
+ JNIEnv* env, jclass, jlong plugin, jobject upstream, jint storage, jstring storeDir, jstring key,
187
+ jlong maxStorageBytes, jstring downloadDir, jobjectArray errorOut) {
188
+ // AlphaVideoGpuReader.Storage ordinals
189
+ enum { kDisk = 0, kMemory = 1 };
190
+ std::string error;
191
+ std::shared_ptr<avg_rs::Reader> reader;
192
+ std::unique_ptr<JavaUpstream> javaUpstream = JavaUpstream::create(env, upstream);
193
+ const auto* table = reinterpret_cast<const RangeSourcePlugin*>(plugin);
194
+ if (!javaUpstream) {
195
+ error = "Cannot call the data source";
196
+ } else if (table) {
197
+ std::shared_ptr<avg_rs::Store> store;
198
+ if (storage == kDisk) {
199
+ store = avg_rs::Store::disk(table, stringFromJava(env, storeDir), maxStorageBytes);
200
+ } else if (storage == kMemory) {
201
+ store = avg_rs::Store::memory(table, stringFromJava(env, storeDir), maxStorageBytes);
202
+ }
203
+ reader = avg_rs::RangeReader::open(table, std::move(javaUpstream), store, stringFromJava(env, key),
204
+ avg_rs::ReaderOptions(), error);
205
+ } else {
206
+ reader = avg_rs::DownloadReader::open(std::move(javaUpstream), stringFromJava(env, downloadDir), error);
207
+ }
208
+ if (!reader) {
209
+ jstring message = env->NewStringUTF(error.c_str());
210
+ env->SetObjectArrayElement(errorOut, 0, message);
211
+ env->DeleteLocalRef(message);
212
+ return 0;
213
+ }
214
+ return reinterpret_cast<jlong>(new ReaderRef(std::move(reader)));
215
+ }
216
+
217
+ JNIEXPORT void JNICALL
218
+ Java_dev_mediacache_alphavideo_gpu_AlphaVideoGpuReader_nativeRelease(JNIEnv*, jclass, jlong reference) {
219
+ delete reinterpret_cast<ReaderRef*>(reference);
220
+ }
221
+
222
+ JNIEXPORT jint JNICALL
223
+ Java_dev_mediacache_alphavideo_gpu_AlphaVideoGpuReader_nativeTryRead(
224
+ JNIEnv* env, jclass, jlong reference, jlong position, jbyteArray buffer, jint offset, jint length) {
225
+ if (length <= 0) return 0;
226
+ std::vector<uint8_t> bytes(static_cast<size_t>(length));
227
+ const int64_t n = readerOf(reference)->tryRead(position, bytes.data(), length);
228
+ if (n > 0) env->SetByteArrayRegion(buffer, offset, static_cast<jsize>(n), reinterpret_cast<const jbyte*>(bytes.data()));
229
+ return static_cast<jint>(n);
230
+ }
231
+
232
+ JNIEXPORT jlong JNICALL
233
+ Java_dev_mediacache_alphavideo_gpu_AlphaVideoGpuReader_nativeSize(JNIEnv*, jclass, jlong reference) {
234
+ return readerOf(reference)->size();
235
+ }
236
+
237
+ JNIEXPORT jstring JNICALL
238
+ Java_dev_mediacache_alphavideo_gpu_AlphaVideoGpuReader_nativeLastError(JNIEnv* env, jclass, jlong reference) {
239
+ return env->NewStringUTF(readerOf(reference)->lastError().c_str());
240
+ }
241
+
242
+ } // extern "C"
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ #include <jni.h>
4
+ #include <memory>
5
+
6
+ // URLs and data sources are read through the range-source add-on, by its C table, or without it
7
+ // through the basic input: a download into a temporary file. Both are an avg_rs::Reader.
8
+ #define RANGE_SOURCE_CLIENT_NAMESPACE avg_rs
9
+ #include "range_source.h"
10
+ #include "range_source_download.h"
11
+
12
+ /** The reader behind an AlphaVideoGpuReader reference, for a holder to keep its own copy. */
13
+ std::shared_ptr<avg_rs::Reader> readerFromReference(jlong reference);
@@ -0,0 +1,43 @@
1
+ package dev.mediacache.alphavideo.gpu;
2
+
3
+ /**
4
+ * JNI bindings to the native library: zstd decompression, the software ASTC decoder and the GL
5
+ * upload paths that {@link AlphaVideoGpuView} uses. Loading the class loads the library.
6
+ */
7
+ final class AlphaVideoGpuNative {
8
+
9
+ static {
10
+ System.loadLibrary("alphavideogpu");
11
+ }
12
+
13
+ private AlphaVideoGpuNative() {}
14
+
15
+ static native void nativeDeleteTexture(int textureId);
16
+ // The file comes as a direct ByteBuffer (mapped, or read from a reader), never on the Java heap
17
+ static native boolean nativeDecompressZstdToBuffer(java.nio.ByteBuffer data, long offset, long compressedLength, java.nio.ByteBuffer outputBuffer, int uncompressedLength);
18
+ static native int[] nativeLoadTextureArray(java.nio.ByteBuffer ktx2Data, boolean useAstc);
19
+
20
+ // Streaming decompression methods (per-instance, returns/accepts stream ID)
21
+ static native long nativeInternalZstdStreamingInit(int fd, long dataOffset, long compressedSize); // Internal KTX2 zstd (supercompressionScheme=2); offset within the descriptor
22
+ static native long nativeReaderZstdStreamingInit(long readerReference, long levelOffset, long compressedSize); // The same over an AlphaVideoGpuReader, without waiting for its bytes
23
+ static native long nativeStreamingDecompressContinueToBuffer(long streamId, java.nio.ByteBuffer outputBuffer, int maxOutputBytes); // Zero-copy; status in the high 32 bits (STREAM_*)
24
+ static native boolean nativeStreamingRestartAt(long streamId, long compressedOffset); // At a zstd frame start in the level
25
+ static native String nativeStreamingError(long streamId); // Why the last call returned -1
26
+ static native void nativeStreamingDecompressEnd(long streamId);
27
+
28
+ /** nativeStreamingDecompressContinueToBuffer statuses: more to come, done, or a reader's bytes on their way. */
29
+ static final int STREAM_MORE = 0;
30
+ static final int STREAM_DONE = 1;
31
+ static final int STREAM_PENDING = 2;
32
+
33
+ // Streaming texture array methods
34
+ static native int nativeCreateTextureArray(int width, int height, int layerCount, boolean useAstc, int glFormat);
35
+ static native boolean nativeUploadTextureArrayLayer(int textureId, int layer, int width, int height, byte[] astcData, boolean useAstc, int glFormat, int blockW, int blockH);
36
+ static native boolean nativeUploadTextureArrayLayerFromBuffer(int textureId, int layer, int width, int height, java.nio.ByteBuffer buffer, int dataLength, boolean useAstc, int glFormat, int blockW, int blockH); // Zero-copy from direct ByteBuffer
37
+ static native boolean nativeUploadTextureArrayLayerFromBufferWithOffset(int textureId, int layer, int width, int height, java.nio.ByteBuffer buffer, int offset, int dataLength, boolean useAstc, int glFormat, int blockW, int blockH); // Zero-copy with offset
38
+
39
+ // PBO-based async upload methods (reduces CPU stalls during texture upload)
40
+ static native boolean nativeInitPBOs(int textureId, int bufferSize); // Initialize double-buffered PBOs for a texture array
41
+ static native void nativeCleanupPBOs(int textureId); // Cleanup PBOs when texture array is destroyed
42
+ static native boolean nativeUploadTextureArrayLayerPBO(int textureId, int layer, int width, int height, java.nio.ByteBuffer buffer, int dataLength, boolean useAstc, int glFormat, int blockW, int blockH); // Async upload via PBO
43
+ }
@@ -0,0 +1,209 @@
1
+ package dev.mediacache.alphavideo.gpu;
2
+
3
+ import android.net.Uri;
4
+
5
+ import java.io.IOException;
6
+ import java.io.InterruptedIOException;
7
+ import java.util.Map;
8
+ import java.util.concurrent.ExecutorService;
9
+ import java.util.concurrent.Executors;
10
+ import java.util.concurrent.atomic.AtomicBoolean;
11
+
12
+ /**
13
+ * A native reader over a {@link Fetcher}: with the range-source add-on, blocks in
14
+ * memory and in an optional store, fetched on the reader's own thread; without it, the file
15
+ * downloaded whole into a temporary file, read as it arrives. One playback owns it, opens it on its
16
+ * worker thread and closes it there; a zstd stream over it keeps its own native reference.
17
+ */
18
+ final class AlphaVideoGpuReader {
19
+
20
+ static {
21
+ System.loadLibrary("alphavideogpu");
22
+ }
23
+
24
+ /** Where a reader keeps what it fetches between plays. */
25
+ enum Storage { DISK, MEMORY, NONE }
26
+
27
+ /** What {@link #tryRead} returns while the bytes are on their way. */
28
+ static final int PENDING = -2;
29
+
30
+ // How long readFully waits between polls
31
+ private static final long POLL_MS = 4;
32
+
33
+ // 0 once closed
34
+ private long handle;
35
+ private final String description;
36
+
37
+ private AlphaVideoGpuReader(long handle, String description) {
38
+ this.handle = handle;
39
+ this.description = description;
40
+ }
41
+
42
+ /**
43
+ * Opens the source at 0 to learn its size and version, unless the store holds it whole. Blocks
44
+ * on the network.
45
+ *
46
+ * @param storeDir the folder of the disk store, or the name of the memory one
47
+ * @param key what the store calls this file
48
+ * @param downloadDir where the file is downloaded without the add-on
49
+ */
50
+ static AlphaVideoGpuReader open(Fetcher source, Uri uri, Map<String, String> headers,
51
+ Storage storage, String storeDir, String key, long maxStorageBytes,
52
+ String downloadDir) throws IOException {
53
+ String[] error = new String[1];
54
+ long handle = nativeOpen(RangeSourceLink.plugin(), new Upstream(source, uri, headers), storage.ordinal(),
55
+ storeDir, key, maxStorageBytes, downloadDir, error);
56
+ if (handle == 0) throw new IOException(error[0] != null ? error[0] : "Cannot open " + uri);
57
+ return new AlphaVideoGpuReader(handle, uri.toString());
58
+ }
59
+
60
+ /** The native reference, for a zstd stream to take its own; 0 once closed. */
61
+ long handle() {
62
+ return handle;
63
+ }
64
+
65
+ /**
66
+ * Reads what is held at {@code position}, without waiting: how many bytes (fewer at a block's
67
+ * end), 0 at the end of the file, -1 if fetching them failed (see {@link #lastError}), or
68
+ * {@link #PENDING}, having asked for them.
69
+ */
70
+ int tryRead(long position, byte[] buffer, int offset, int length) {
71
+ if (handle == 0) return -1;
72
+ return nativeTryRead(handle, position, buffer, offset, length);
73
+ }
74
+
75
+ /**
76
+ * Reads {@code length} bytes at {@code position}, fewer only at the end of the file. Waits for
77
+ * them; throws if they can't be fetched, or once {@code cancelled} is set.
78
+ */
79
+ int readFully(long position, byte[] buffer, int offset, int length, AtomicBoolean cancelled) throws IOException {
80
+ int total = 0;
81
+ while (total < length) {
82
+ int n = tryRead(position + total, buffer, offset + total, length - total);
83
+ if (n == PENDING) {
84
+ if (cancelled.get()) throw new InterruptedIOException("Cancelled");
85
+ try {
86
+ Thread.sleep(POLL_MS);
87
+ } catch (InterruptedException e) {
88
+ Thread.currentThread().interrupt();
89
+ throw new InterruptedIOException("Interrupted");
90
+ }
91
+ continue;
92
+ }
93
+ if (n < 0) throw new IOException("Reading " + description + " failed: " + lastError());
94
+ if (n == 0) break;
95
+ total += n;
96
+ }
97
+ return total;
98
+ }
99
+
100
+ /** The file's size, or -1 while it isn't known. */
101
+ long size() {
102
+ return handle != 0 ? nativeSize(handle) : -1;
103
+ }
104
+
105
+ String lastError() {
106
+ String error = handle != 0 ? nativeLastError(handle) : null;
107
+ return error != null && !error.isEmpty() ? error : "the reader is closed";
108
+ }
109
+
110
+ /** Drops this reference; the fetch stops once a zstd stream over it has ended too. */
111
+ void close() {
112
+ long h = handle;
113
+ handle = 0;
114
+ if (h != 0) nativeRelease(h);
115
+ }
116
+
117
+ @Override
118
+ public String toString() {
119
+ return description;
120
+ }
121
+
122
+ /**
123
+ * The fetcher as the native reader calls it, on the reader's fetch thread, with failures
124
+ * as return values.
125
+ */
126
+ static final class Upstream {
127
+ // cancel() comes from whichever thread closes the reader, and an HTTPS disconnect can
128
+ // write to the socket.
129
+ private static final ExecutorService cancelExecutor = Executors.newCachedThreadPool(r -> {
130
+ Thread t = new Thread(r, "AlphaVideoGpuCancel");
131
+ t.setDaemon(true);
132
+ return t;
133
+ });
134
+
135
+ private final Fetcher source;
136
+ private final Uri uri;
137
+ private final Map<String, String> headers;
138
+ // Read by native code after open() and failures
139
+ String etag;
140
+ String lastModified;
141
+ String error;
142
+
143
+ Upstream(Fetcher source, Uri uri, Map<String, String> headers) {
144
+ this.source = source;
145
+ this.uri = uri;
146
+ this.headers = headers;
147
+ }
148
+
149
+ /** The file's size, -1 if not known, or -2 with {@link #error} set. */
150
+ long open(long position) {
151
+ try {
152
+ Fetcher.Info info = source.open(uri, position, headers);
153
+ etag = info.etag;
154
+ lastModified = info.lastModified;
155
+ return info.size;
156
+ } catch (Exception e) {
157
+ error = describe(e);
158
+ return -2;
159
+ }
160
+ }
161
+
162
+ /** How many bytes were read into {@code buffer}, 0 at the end, or -1 with {@link #error} set. */
163
+ int read(byte[] buffer, int length) {
164
+ try {
165
+ int n;
166
+ do {
167
+ n = source.read(buffer, 0, length);
168
+ } while (n == 0);
169
+ return n < 0 ? 0 : n;
170
+ } catch (Exception e) {
171
+ error = describe(e);
172
+ return -1;
173
+ }
174
+ }
175
+
176
+ void close() {
177
+ try {
178
+ source.close();
179
+ } catch (Exception ignored) {
180
+ }
181
+ }
182
+
183
+ void cancel() {
184
+ cancelExecutor.execute(() -> {
185
+ try {
186
+ source.cancel();
187
+ } catch (Exception ignored) {
188
+ }
189
+ });
190
+ }
191
+
192
+ private static String describe(Exception e) {
193
+ return e.getMessage() != null ? e.getMessage() : e.toString();
194
+ }
195
+ }
196
+
197
+ /**
198
+ * @param plugin range-source's table ({@link RangeSourceLink#plugin}), or 0 to download the file
199
+ * @param storage a {@link Storage} ordinal
200
+ * @param error set to why, when it returns 0
201
+ * @return a reference to the reader, or 0
202
+ */
203
+ private static native long nativeOpen(long plugin, Upstream upstream, int storage, String storeDir, String key,
204
+ long maxStorageBytes, String downloadDir, String[] error);
205
+ private static native void nativeRelease(long reference);
206
+ private static native int nativeTryRead(long reference, long position, byte[] buffer, int offset, int length);
207
+ private static native long nativeSize(long reference);
208
+ private static native String nativeLastError(long reference);
209
+ }