react-native-webgpu 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/android/cpp/AndroidPlatformContext.h +42 -8
  2. package/android/cpp/AndroidVideoPlayer.h +307 -0
  3. package/android/src/main/java/com/webgpu/WebGPUModule.java +10 -0
  4. package/android/src/main/java/com/webgpu/WebGPUVideoPlayer.java +420 -0
  5. package/apple/AppleVideoPlayer.mm +157 -47
  6. package/cpp/rnwgpu/PlatformContext.h +25 -5
  7. package/cpp/rnwgpu/api/GPUDevice.cpp +2 -1
  8. package/cpp/rnwgpu/api/GPUDevice.h +6 -0
  9. package/cpp/rnwgpu/api/GPUQueue.cpp +80 -10
  10. package/cpp/rnwgpu/api/GPUQueue.h +15 -4
  11. package/cpp/rnwgpu/api/VideoFrameBlit.h +355 -0
  12. package/cpp/rnwgpu/api/VideoPlayer.h +50 -3
  13. package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +30 -3
  14. package/lib/commonjs/importDevice.js +2 -2
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/main/index.js +12 -0
  17. package/lib/commonjs/main/index.js.map +1 -1
  18. package/lib/commonjs/main/index.web.js +11 -0
  19. package/lib/commonjs/main/index.web.js.map +1 -1
  20. package/lib/commonjs/video.js +58 -0
  21. package/lib/commonjs/video.js.map +1 -0
  22. package/lib/module/importDevice.js +2 -2
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/main/index.js +1 -0
  25. package/lib/module/main/index.js.map +1 -1
  26. package/lib/module/main/index.web.js +1 -0
  27. package/lib/module/main/index.web.js.map +1 -1
  28. package/lib/module/video.js +49 -0
  29. package/lib/module/video.js.map +1 -0
  30. package/lib/typescript/lib/commonjs/video.d.ts +30 -0
  31. package/lib/typescript/lib/commonjs/video.d.ts.map +1 -0
  32. package/lib/typescript/lib/module/main/index.d.ts +1 -0
  33. package/lib/typescript/lib/module/main/index.web.d.ts +1 -0
  34. package/lib/typescript/lib/module/video.d.ts +4 -0
  35. package/lib/typescript/lib/module/video.d.ts.map +1 -0
  36. package/lib/typescript/src/importDevice.d.ts +2 -2
  37. package/lib/typescript/src/index.d.ts +5 -3
  38. package/lib/typescript/src/index.d.ts.map +1 -1
  39. package/lib/typescript/src/main/index.d.ts +1 -0
  40. package/lib/typescript/src/main/index.d.ts.map +1 -1
  41. package/lib/typescript/src/main/index.web.d.ts +1 -0
  42. package/lib/typescript/src/main/index.web.d.ts.map +1 -1
  43. package/lib/typescript/src/types.d.ts +19 -0
  44. package/lib/typescript/src/types.d.ts.map +1 -1
  45. package/lib/typescript/src/video.d.ts +41 -0
  46. package/lib/typescript/src/video.d.ts.map +1 -0
  47. package/package.json +1 -1
  48. package/src/importDevice.ts +2 -2
  49. package/src/index.tsx +28 -3
  50. package/src/main/index.tsx +1 -0
  51. package/src/main/index.web.tsx +1 -0
  52. package/src/types.ts +60 -7
  53. package/src/video.ts +69 -0
@@ -13,6 +13,7 @@
13
13
 
14
14
  #include "webgpu/webgpu_cpp.h"
15
15
 
16
+ #include "AndroidVideoPlayer.h"
16
17
  #include "PlatformContext.h"
17
18
  #include "RNWebGPUManager.h"
18
19
 
@@ -112,8 +113,8 @@ public:
112
113
  }
113
114
  // Ownership passes to Java; Java_com_webgpu_WebGPUModule_onViewSnapshot
114
115
  // (cpp-adapter.cpp) reclaims it.
115
- auto *callbacks = new ViewSnapshotCallbacks{std::move(onSuccess),
116
- std::move(onError)};
116
+ auto *callbacks =
117
+ new ViewSnapshotCallbacks{std::move(onSuccess), std::move(onError)};
117
118
  env->CallVoidMethod(_module, method, static_cast<jint>(request.viewTag),
118
119
  static_cast<jdouble>(request.sourceX),
119
120
  static_cast<jdouble>(request.sourceY),
@@ -236,8 +237,9 @@ public:
236
237
  result.height = static_cast<int>(bitmapInfo.height);
237
238
  result.data.resize(bitmapInfo.height * bitmapInfo.stride);
238
239
  memcpy(result.data.data(), bitmapPixels, result.data.size());
239
- // BitmapFactory hands back premultiplied ARGB_8888 pixels; createImageBitmap
240
- // converts to the representation requested by premultiplyAlpha.
240
+ // BitmapFactory hands back premultiplied ARGB_8888 pixels;
241
+ // createImageBitmap converts to the representation requested by
242
+ // premultiplyAlpha.
241
243
  result.premultiplied = true;
242
244
 
243
245
  AndroidBitmap_unlockPixels(env, bitmap);
@@ -339,12 +341,44 @@ public:
339
341
  return handle;
340
342
  }
341
343
 
344
+ // The decoder writes its native YUV layout whatever format is requested:
345
+ // the frames are sampled through importExternalTexture (see
346
+ // WebGPUVideoPlayer.java).
342
347
  std::unique_ptr<IVideoPlayer>
343
- createVideoPlayer(const std::string & /*path*/,
348
+ createVideoPlayer(const std::string &path,
344
349
  VideoPixelFormat /*format*/) override {
345
- // TODO: implement using MediaCodec -> ImageReader (AHardwareBuffer mode).
346
- throw std::runtime_error(
347
- "createVideoPlayer is not yet implemented on Android.");
350
+ if (!_module) {
351
+ throw std::runtime_error("createVideoPlayer: the WebGPU module is gone");
352
+ }
353
+ jni::Environment::ensureCurrentThreadIsAttached();
354
+ JNIEnv *env = facebook::jni::Environment::current();
355
+ if (!env) {
356
+ throw std::runtime_error(
357
+ "createVideoPlayer: couldn't get the JNI environment");
358
+ }
359
+ jclass moduleClass = env->GetObjectClass(_module);
360
+ jmethodID method =
361
+ env->GetMethodID(moduleClass, "createVideoPlayer",
362
+ "(Ljava/lang/String;)Lcom/webgpu/WebGPUVideoPlayer;");
363
+ env->DeleteLocalRef(moduleClass);
364
+ if (!method) {
365
+ env->ExceptionClear();
366
+ throw std::runtime_error(
367
+ "createVideoPlayer: WebGPUModule.createVideoPlayer not found");
368
+ }
369
+ jstring jPath = env->NewStringUTF(path.c_str());
370
+ jobject player = env->CallObjectMethod(_module, method, jPath);
371
+ env->DeleteLocalRef(jPath);
372
+ if (env->ExceptionCheck()) {
373
+ throw std::runtime_error("createVideoPlayer: " +
374
+ describeAndClearJavaException(env));
375
+ }
376
+ if (player == nullptr) {
377
+ throw std::runtime_error("createVideoPlayer: no player was created");
378
+ }
379
+ auto result = std::make_unique<AndroidVideoPlayer>(env, player);
380
+ env->DeleteLocalRef(player);
381
+ return result;
348
382
  }
349
383
 
350
384
  std::string writeTestVideoFile() override {
@@ -0,0 +1,307 @@
1
+ #pragma once
2
+
3
+ #include <android/hardware_buffer.h>
4
+ #include <android/hardware_buffer_jni.h>
5
+ #include <fbjni/fbjni.h>
6
+ #include <jni.h>
7
+
8
+ #include <memory>
9
+ #include <stdexcept>
10
+ #include <string>
11
+ #include <utility>
12
+
13
+ #include "PlatformContext.h"
14
+
15
+ namespace rnwgpu {
16
+
17
+ // The message of the pending Java exception, which is cleared.
18
+ inline std::string describeAndClearJavaException(JNIEnv *env) {
19
+ jthrowable throwable = env->ExceptionOccurred();
20
+ if (throwable == nullptr) {
21
+ return "unknown error";
22
+ }
23
+ env->ExceptionClear();
24
+ std::string message = "Java exception";
25
+ jclass throwableClass = env->GetObjectClass(throwable);
26
+ jmethodID getMessage =
27
+ env->GetMethodID(throwableClass, "getMessage", "()Ljava/lang/String;");
28
+ if (getMessage != nullptr) {
29
+ auto text =
30
+ static_cast<jstring>(env->CallObjectMethod(throwable, getMessage));
31
+ if (env->ExceptionCheck()) {
32
+ env->ExceptionClear();
33
+ } else if (text != nullptr) {
34
+ const char *chars = env->GetStringUTFChars(text, nullptr);
35
+ if (chars != nullptr) {
36
+ message = chars;
37
+ env->ReleaseStringUTFChars(text, chars);
38
+ }
39
+ env->DeleteLocalRef(text);
40
+ }
41
+ }
42
+ env->DeleteLocalRef(throwableClass);
43
+ env->DeleteLocalRef(throwable);
44
+ return message;
45
+ }
46
+
47
+ // A global reference to the Java player, shared between AndroidVideoPlayer
48
+ // and the deleters of the frames it handed out (they close each frame's
49
+ // Image through the player). The reference goes away with the last of them.
50
+ class JavaVideoPlayerRef {
51
+ public:
52
+ JavaVideoPlayerRef(JNIEnv *env, jobject local)
53
+ : _object(env->NewGlobalRef(local)) {}
54
+ ~JavaVideoPlayerRef() {
55
+ facebook::jni::Environment::ensureCurrentThreadIsAttached();
56
+ JNIEnv *env = facebook::jni::Environment::current();
57
+ if (env != nullptr && _object != nullptr) {
58
+ env->DeleteGlobalRef(_object);
59
+ }
60
+ }
61
+ JavaVideoPlayerRef(const JavaVideoPlayerRef &) = delete;
62
+ JavaVideoPlayerRef &operator=(const JavaVideoPlayerRef &) = delete;
63
+
64
+ jobject get() const { return _object; }
65
+
66
+ private:
67
+ jobject _object;
68
+ };
69
+
70
+ // IVideoPlayer backed by com.webgpu.WebGPUVideoPlayer: MediaPlayer decodes
71
+ // into an ImageReader whose buffers the GPU samples. The frames keep the
72
+ // decoder's native YUV layout (reported as NV12), for importExternalTexture.
73
+ class AndroidVideoPlayer : public IVideoPlayer {
74
+ public:
75
+ AndroidVideoPlayer(JNIEnv *env, jobject player)
76
+ : _player(std::make_shared<JavaVideoPlayerRef>(env, player)) {
77
+ jclass playerClass = env->GetObjectClass(player);
78
+ _copyLatestFrame =
79
+ method(env, playerClass, "copyLatestFrame", "()Landroid/media/Image;");
80
+ _closeImage =
81
+ method(env, playerClass, "closeImage", "(Landroid/media/Image;)V");
82
+ _play = method(env, playerClass, "play", "()V");
83
+ _pause = method(env, playerClass, "pause", "()V");
84
+ _getPaused = method(env, playerClass, "getPaused", "()Z");
85
+ _getCurrentTime = method(env, playerClass, "getCurrentTime", "()D");
86
+ _seek = method(env, playerClass, "seek", "(D)V");
87
+ _getDuration = method(env, playerClass, "getDuration", "()D");
88
+ _getVolume = method(env, playerClass, "getVolume", "()D");
89
+ _setVolume = method(env, playerClass, "setVolume", "(D)V");
90
+ _getLoop = method(env, playerClass, "getLoop", "()Z");
91
+ _setLoop = method(env, playerClass, "setLoop", "(Z)V");
92
+ _getVideoWidth = method(env, playerClass, "getVideoWidth", "()I");
93
+ _getVideoHeight = method(env, playerClass, "getVideoHeight", "()I");
94
+ _getRotation = method(env, playerClass, "getRotation", "()I");
95
+ _getFrameRate = method(env, playerClass, "getFrameRate", "()D");
96
+ _release = method(env, playerClass, "release", "()V");
97
+ env->DeleteLocalRef(playerClass);
98
+
99
+ jclass imageClass = env->FindClass("android/media/Image");
100
+ _getHardwareBuffer = method(env, imageClass, "getHardwareBuffer",
101
+ "()Landroid/hardware/HardwareBuffer;");
102
+ env->DeleteLocalRef(imageClass);
103
+ }
104
+
105
+ ~AndroidVideoPlayer() override {
106
+ JNIEnv *env = attach();
107
+ env->CallVoidMethod(_player->get(), _release);
108
+ if (env->ExceptionCheck()) {
109
+ env->ExceptionClear();
110
+ }
111
+ }
112
+
113
+ VideoFrameHandle copyLatestFrame() override {
114
+ JNIEnv *env = attach();
115
+ jobject image = env->CallObjectMethod(_player->get(), _copyLatestFrame);
116
+ if (env->ExceptionCheck()) {
117
+ throw std::runtime_error("copyLatestFrame: " +
118
+ describeAndClearJavaException(env));
119
+ }
120
+ if (image == nullptr) {
121
+ return {};
122
+ }
123
+
124
+ jobject hardwareBuffer = env->CallObjectMethod(image, _getHardwareBuffer);
125
+ if (env->ExceptionCheck() || hardwareBuffer == nullptr) {
126
+ env->ExceptionClear();
127
+ closeImage(env, image);
128
+ env->DeleteLocalRef(image);
129
+ throw std::runtime_error(
130
+ "copyLatestFrame: the decoded frame has no HardwareBuffer");
131
+ }
132
+ AHardwareBuffer *buffer =
133
+ AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
134
+ env->DeleteLocalRef(hardwareBuffer);
135
+
136
+ AHardwareBuffer_Desc desc = {};
137
+ AHardwareBuffer_describe(buffer, &desc);
138
+ if ((desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) == 0) {
139
+ closeImage(env, image);
140
+ env->DeleteLocalRef(image);
141
+ throw std::runtime_error(
142
+ "copyLatestFrame: the decoder allocated frames without "
143
+ "AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, so the GPU cannot sample "
144
+ "them");
145
+ }
146
+
147
+ // The frame owns a reference to the buffer and keeps the Image acquired
148
+ // until it is released; closing the Image hands the buffer back to the
149
+ // decoder.
150
+ AHardwareBuffer_acquire(buffer);
151
+ jobject imageRef = env->NewGlobalRef(image);
152
+ env->DeleteLocalRef(image);
153
+
154
+ VideoFrameHandle handle;
155
+ handle.handle = static_cast<void *>(buffer);
156
+ handle.width = desc.width;
157
+ handle.height = desc.height;
158
+ handle.pixelFormat = isRgbFormat(desc.format) ? VideoPixelFormat::BGRA8
159
+ : VideoPixelFormat::NV12;
160
+ auto player = _player;
161
+ jmethodID closeImageMethod = _closeImage;
162
+ handle.deleter = [player, closeImageMethod, imageRef, buffer]() {
163
+ JNIEnv *env = attach();
164
+ env->CallVoidMethod(player->get(), closeImageMethod, imageRef);
165
+ if (env->ExceptionCheck()) {
166
+ env->ExceptionClear();
167
+ }
168
+ env->DeleteGlobalRef(imageRef);
169
+ AHardwareBuffer_release(buffer);
170
+ };
171
+ return handle;
172
+ }
173
+
174
+ void play() override { callVoid(_play); }
175
+ void pause() override { callVoid(_pause); }
176
+ bool paused() override { return callBool(_getPaused); }
177
+
178
+ double currentTime() override { return callDouble(_getCurrentTime); }
179
+ void seek(double seconds) override {
180
+ JNIEnv *env = attach();
181
+ env->CallVoidMethod(_player->get(), _seek, static_cast<jdouble>(seconds));
182
+ clearException(env);
183
+ }
184
+ double duration() override { return callDouble(_getDuration); }
185
+
186
+ double volume() override { return callDouble(_getVolume); }
187
+ void setVolume(double volume) override {
188
+ JNIEnv *env = attach();
189
+ env->CallVoidMethod(_player->get(), _setVolume,
190
+ static_cast<jdouble>(volume));
191
+ clearException(env);
192
+ }
193
+
194
+ bool loop() override { return callBool(_getLoop); }
195
+ void setLoop(bool loop) override {
196
+ JNIEnv *env = attach();
197
+ env->CallVoidMethod(_player->get(), _setLoop, static_cast<jboolean>(loop));
198
+ clearException(env);
199
+ }
200
+
201
+ uint32_t videoWidth() override {
202
+ return static_cast<uint32_t>(callInt(_getVideoWidth));
203
+ }
204
+ uint32_t videoHeight() override {
205
+ return static_cast<uint32_t>(callInt(_getVideoHeight));
206
+ }
207
+ int rotation() override { return callInt(_getRotation); }
208
+ double frameRate() override { return callDouble(_getFrameRate); }
209
+
210
+ private:
211
+ static JNIEnv *attach() {
212
+ facebook::jni::Environment::ensureCurrentThreadIsAttached();
213
+ JNIEnv *env = facebook::jni::Environment::current();
214
+ if (env == nullptr) {
215
+ throw std::runtime_error("VideoPlayer: no JNI environment");
216
+ }
217
+ return env;
218
+ }
219
+
220
+ static jmethodID method(JNIEnv *env, jclass cls, const char *name,
221
+ const char *signature) {
222
+ jmethodID id = env->GetMethodID(cls, name, signature);
223
+ if (id == nullptr) {
224
+ env->ExceptionClear();
225
+ throw std::runtime_error(std::string("VideoPlayer: method ") + name +
226
+ " not found");
227
+ }
228
+ return id;
229
+ }
230
+
231
+ static void clearException(JNIEnv *env) {
232
+ if (env->ExceptionCheck()) {
233
+ env->ExceptionClear();
234
+ }
235
+ }
236
+
237
+ // Single-plane RGB buffers sample as regular textures; everything else
238
+ // (YUV 4:2:0, P010, implementation-defined codec formats) goes through
239
+ // Vulkan's YCbCr conversion as an external texture.
240
+ static bool isRgbFormat(uint32_t format) {
241
+ switch (format) {
242
+ case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
243
+ case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
244
+ case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
245
+ case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
246
+ case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
247
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
248
+ return true;
249
+ default:
250
+ return false;
251
+ }
252
+ }
253
+
254
+ void closeImage(JNIEnv *env, jobject image) {
255
+ env->CallVoidMethod(_player->get(), _closeImage, image);
256
+ clearException(env);
257
+ }
258
+
259
+ void callVoid(jmethodID id) {
260
+ JNIEnv *env = attach();
261
+ env->CallVoidMethod(_player->get(), id);
262
+ clearException(env);
263
+ }
264
+
265
+ bool callBool(jmethodID id) {
266
+ JNIEnv *env = attach();
267
+ bool result = env->CallBooleanMethod(_player->get(), id) == JNI_TRUE;
268
+ clearException(env);
269
+ return result;
270
+ }
271
+
272
+ int callInt(jmethodID id) {
273
+ JNIEnv *env = attach();
274
+ int result = env->CallIntMethod(_player->get(), id);
275
+ clearException(env);
276
+ return result;
277
+ }
278
+
279
+ double callDouble(jmethodID id) {
280
+ JNIEnv *env = attach();
281
+ double result = env->CallDoubleMethod(_player->get(), id);
282
+ clearException(env);
283
+ return result;
284
+ }
285
+
286
+ std::shared_ptr<JavaVideoPlayerRef> _player;
287
+ jmethodID _copyLatestFrame;
288
+ jmethodID _closeImage;
289
+ jmethodID _play;
290
+ jmethodID _pause;
291
+ jmethodID _getPaused;
292
+ jmethodID _getCurrentTime;
293
+ jmethodID _seek;
294
+ jmethodID _getDuration;
295
+ jmethodID _getVolume;
296
+ jmethodID _setVolume;
297
+ jmethodID _getLoop;
298
+ jmethodID _setLoop;
299
+ jmethodID _getVideoWidth;
300
+ jmethodID _getVideoHeight;
301
+ jmethodID _getRotation;
302
+ jmethodID _getFrameRate;
303
+ jmethodID _release;
304
+ jmethodID _getHardwareBuffer;
305
+ };
306
+
307
+ } // namespace rnwgpu
@@ -115,4 +115,14 @@ public class WebGPUModule extends NativeWebGPUModuleSpec {
115
115
 
116
116
  @DoNotStrip
117
117
  private native void onViewSnapshot(long callback, Bitmap bitmap, String error);
118
+
119
+ /**
120
+ * Called from C++ (AndroidPlatformContext::createVideoPlayer) for
121
+ * RNWebGPU.createVideoPlayer. Throws when the source cannot be opened; the
122
+ * caller turns the exception into a JS error.
123
+ */
124
+ @DoNotStrip
125
+ WebGPUVideoPlayer createVideoPlayer(String source) {
126
+ return new WebGPUVideoPlayer(getReactApplicationContext(), source);
127
+ }
118
128
  }