react-native-webgpu 0.10.5 → 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.
- package/android/cpp/AndroidPlatformContext.h +100 -9
- package/android/cpp/AndroidVideoPlayer.h +307 -0
- package/android/cpp/cpp-adapter.cpp +66 -6
- package/android/src/main/java/com/webgpu/ViewSnapshot.java +162 -0
- package/android/src/main/java/com/webgpu/WebGPUModule.java +78 -0
- package/android/src/main/java/com/webgpu/WebGPUVideoPlayer.java +420 -0
- package/apple/ApplePlatformContext.h +14 -1
- package/apple/ApplePlatformContext.mm +176 -1
- package/apple/AppleVideoPlayer.mm +157 -47
- package/apple/WebGPUModule.mm +20 -1
- package/cpp/rnwgpu/PlatformContext.h +76 -1
- package/cpp/rnwgpu/RNWebGPUManager.cpp +7 -0
- package/cpp/rnwgpu/api/GPUDevice.cpp +2 -1
- package/cpp/rnwgpu/api/GPUDevice.h +6 -0
- package/cpp/rnwgpu/api/GPUQueue.cpp +261 -4
- package/cpp/rnwgpu/api/GPUQueue.h +29 -2
- package/cpp/rnwgpu/api/VideoFrameBlit.h +355 -0
- package/cpp/rnwgpu/api/VideoPlayer.h +50 -3
- package/cpp/rnwgpu/api/descriptors/GPUDrawElementImageDestination.h +60 -0
- package/cpp/rnwgpu/api/descriptors/GPUDrawElementImageSource.h +106 -0
- package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +30 -3
- package/lib/commonjs/importDevice.js +2 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/main/index.js +45 -0
- package/lib/commonjs/main/index.js.map +1 -1
- package/lib/commonjs/main/index.web.js +11 -0
- package/lib/commonjs/main/index.web.js.map +1 -1
- package/lib/commonjs/video.js +58 -0
- package/lib/commonjs/video.js.map +1 -0
- package/lib/module/importDevice.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/main/index.js +34 -0
- package/lib/module/main/index.js.map +1 -1
- package/lib/module/main/index.web.js +1 -0
- package/lib/module/main/index.web.js.map +1 -1
- package/lib/module/video.js +49 -0
- package/lib/module/video.js.map +1 -0
- package/lib/typescript/lib/commonjs/video.d.ts +30 -0
- package/lib/typescript/lib/commonjs/video.d.ts.map +1 -0
- package/lib/typescript/lib/module/main/index.d.ts +1 -0
- package/lib/typescript/lib/module/main/index.web.d.ts +1 -0
- package/lib/typescript/lib/module/video.d.ts +4 -0
- package/lib/typescript/lib/module/video.d.ts.map +1 -0
- package/lib/typescript/src/importDevice.d.ts +2 -2
- package/lib/typescript/src/index.d.ts +8 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/main/index.d.ts +1 -0
- package/lib/typescript/src/main/index.d.ts.map +1 -1
- package/lib/typescript/src/main/index.web.d.ts +1 -0
- package/lib/typescript/src/main/index.web.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +32 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/video.d.ts +41 -0
- package/lib/typescript/src/video.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/importDevice.ts +2 -2
- package/src/index.tsx +57 -3
- package/src/main/index.tsx +50 -0
- package/src/main/index.web.tsx +1 -0
- package/src/types.ts +89 -7
- 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
|
|
|
@@ -21,9 +22,19 @@ namespace rnwgpu {
|
|
|
21
22
|
namespace jsi = facebook::jsi;
|
|
22
23
|
namespace jni = facebook::jni;
|
|
23
24
|
|
|
25
|
+
// The completion callbacks of one snapshotView request, heap-allocated and
|
|
26
|
+
// carried through Java as an opaque pointer (WebGPUModule.snapshotView ->
|
|
27
|
+
// onViewSnapshot), which hands them back exactly once.
|
|
28
|
+
struct ViewSnapshotCallbacks {
|
|
29
|
+
std::function<void(ImageData)> onSuccess;
|
|
30
|
+
std::function<void(std::string)> onError;
|
|
31
|
+
};
|
|
32
|
+
|
|
24
33
|
class AndroidPlatformContext : public PlatformContext {
|
|
25
34
|
private:
|
|
26
35
|
jobject _blobModule;
|
|
36
|
+
// Global ref to the com.webgpu.WebGPUModule instance (view snapshots).
|
|
37
|
+
jobject _module;
|
|
27
38
|
|
|
28
39
|
std::vector<uint8_t> resolveBlob(JNIEnv *env, const std::string &blobId,
|
|
29
40
|
double offset, double size) {
|
|
@@ -63,14 +74,61 @@ private:
|
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
public:
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
// Both arguments are global refs; the context releases them.
|
|
78
|
+
AndroidPlatformContext(jobject blobModule, jobject module)
|
|
79
|
+
: _blobModule(blobModule), _module(module) {}
|
|
68
80
|
~AndroidPlatformContext() {
|
|
81
|
+
JNIEnv *env = facebook::jni::Environment::current();
|
|
69
82
|
if (_blobModule) {
|
|
70
|
-
JNIEnv *env = facebook::jni::Environment::current();
|
|
71
83
|
env->DeleteGlobalRef(_blobModule);
|
|
72
84
|
_blobModule = nullptr;
|
|
73
85
|
}
|
|
86
|
+
if (_module) {
|
|
87
|
+
env->DeleteGlobalRef(_module);
|
|
88
|
+
_module = nullptr;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void snapshotView(const ViewSnapshotRequest &request,
|
|
93
|
+
std::function<void(ImageData)> onSuccess,
|
|
94
|
+
std::function<void(std::string)> onError) override {
|
|
95
|
+
if (!_module) {
|
|
96
|
+
onError("drawElementImageToTexture: the WebGPU module is gone");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
jni::Environment::ensureCurrentThreadIsAttached();
|
|
100
|
+
JNIEnv *env = facebook::jni::Environment::current();
|
|
101
|
+
if (!env) {
|
|
102
|
+
onError("drawElementImageToTexture: couldn't get the JNI environment");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
jclass moduleClass = env->GetObjectClass(_module);
|
|
106
|
+
jmethodID method =
|
|
107
|
+
env->GetMethodID(moduleClass, "snapshotView", "(IDDDDIIJ)V");
|
|
108
|
+
env->DeleteLocalRef(moduleClass);
|
|
109
|
+
if (!method) {
|
|
110
|
+
env->ExceptionClear();
|
|
111
|
+
onError("drawElementImageToTexture: WebGPUModule.snapshotView not found");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Ownership passes to Java; Java_com_webgpu_WebGPUModule_onViewSnapshot
|
|
115
|
+
// (cpp-adapter.cpp) reclaims it.
|
|
116
|
+
auto *callbacks =
|
|
117
|
+
new ViewSnapshotCallbacks{std::move(onSuccess), std::move(onError)};
|
|
118
|
+
env->CallVoidMethod(_module, method, static_cast<jint>(request.viewTag),
|
|
119
|
+
static_cast<jdouble>(request.sourceX),
|
|
120
|
+
static_cast<jdouble>(request.sourceY),
|
|
121
|
+
static_cast<jdouble>(request.sourceWidth),
|
|
122
|
+
static_cast<jdouble>(request.sourceHeight),
|
|
123
|
+
static_cast<jint>(request.width),
|
|
124
|
+
static_cast<jint>(request.height),
|
|
125
|
+
reinterpret_cast<jlong>(callbacks));
|
|
126
|
+
if (env->ExceptionCheck()) {
|
|
127
|
+
env->ExceptionClear();
|
|
128
|
+
auto fail = std::move(callbacks->onError);
|
|
129
|
+
delete callbacks;
|
|
130
|
+
fail("drawElementImageToTexture: WebGPUModule.snapshotView threw");
|
|
131
|
+
}
|
|
74
132
|
}
|
|
75
133
|
|
|
76
134
|
wgpu::Surface makeSurface(wgpu::Instance instance, void *window, int width,
|
|
@@ -179,8 +237,9 @@ public:
|
|
|
179
237
|
result.height = static_cast<int>(bitmapInfo.height);
|
|
180
238
|
result.data.resize(bitmapInfo.height * bitmapInfo.stride);
|
|
181
239
|
memcpy(result.data.data(), bitmapPixels, result.data.size());
|
|
182
|
-
// BitmapFactory hands back premultiplied ARGB_8888 pixels;
|
|
183
|
-
// converts to the representation requested by
|
|
240
|
+
// BitmapFactory hands back premultiplied ARGB_8888 pixels;
|
|
241
|
+
// createImageBitmap converts to the representation requested by
|
|
242
|
+
// premultiplyAlpha.
|
|
184
243
|
result.premultiplied = true;
|
|
185
244
|
|
|
186
245
|
AndroidBitmap_unlockPixels(env, bitmap);
|
|
@@ -282,12 +341,44 @@ public:
|
|
|
282
341
|
return handle;
|
|
283
342
|
}
|
|
284
343
|
|
|
344
|
+
// The decoder writes its native YUV layout whatever format is requested:
|
|
345
|
+
// the frames are sampled through importExternalTexture (see
|
|
346
|
+
// WebGPUVideoPlayer.java).
|
|
285
347
|
std::unique_ptr<IVideoPlayer>
|
|
286
|
-
createVideoPlayer(const std::string &
|
|
348
|
+
createVideoPlayer(const std::string &path,
|
|
287
349
|
VideoPixelFormat /*format*/) override {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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;
|
|
291
382
|
}
|
|
292
383
|
|
|
293
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
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
#include <cstring>
|
|
1
2
|
#include <memory>
|
|
3
|
+
#include <string>
|
|
2
4
|
#include <unordered_map>
|
|
5
|
+
#include <utility>
|
|
3
6
|
|
|
4
7
|
#include <fbjni/fbjni.h>
|
|
5
8
|
#include <jni.h>
|
|
6
9
|
#include <jsi/jsi.h>
|
|
7
10
|
|
|
8
11
|
#include <ReactCommon/CallInvokerHolder.h>
|
|
12
|
+
#include <android/bitmap.h>
|
|
9
13
|
#include <android/hardware_buffer_jni.h>
|
|
10
14
|
#include <android/native_window_jni.h>
|
|
11
15
|
#include <webgpu/webgpu_cpp.h>
|
|
@@ -19,21 +23,77 @@
|
|
|
19
23
|
std::shared_ptr<rnwgpu::RNWebGPUManager> manager;
|
|
20
24
|
|
|
21
25
|
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative(
|
|
22
|
-
JNIEnv *env, jobject
|
|
23
|
-
jobject
|
|
26
|
+
JNIEnv *env, jobject thiz, jlong jsRuntime, jobject jsCallInvokerHolder,
|
|
27
|
+
jobject blobModule) {
|
|
24
28
|
auto runtime = reinterpret_cast<facebook::jsi::Runtime *>(jsRuntime);
|
|
25
29
|
jobject globalBlobModule = env->NewGlobalRef(blobModule);
|
|
30
|
+
jobject globalModule = env->NewGlobalRef(thiz);
|
|
26
31
|
auto jsCallInvoker{
|
|
27
32
|
facebook::jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>{
|
|
28
33
|
reinterpret_cast<facebook::react::CallInvokerHolder::javaobject>(
|
|
29
|
-
jsCallInvokerHolder)} -> cthis()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
std::make_shared<rnwgpu::AndroidPlatformContext>(globalBlobModule);
|
|
34
|
+
jsCallInvokerHolder)} -> cthis()->getCallInvoker()};
|
|
35
|
+
auto platformContext = std::make_shared<rnwgpu::AndroidPlatformContext>(
|
|
36
|
+
globalBlobModule, globalModule);
|
|
33
37
|
manager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, jsCallInvoker,
|
|
34
38
|
platformContext);
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
// Completion of WebGPUModule.snapshotView (UI thread). Reclaims the callbacks
|
|
42
|
+
// handed to Java and delivers either the bitmap's pixels or the error.
|
|
43
|
+
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_onViewSnapshot(
|
|
44
|
+
JNIEnv *env, jobject /* this */, jlong callbackPointer, jobject bitmap,
|
|
45
|
+
jstring error) {
|
|
46
|
+
std::unique_ptr<rnwgpu::ViewSnapshotCallbacks> callbacks(
|
|
47
|
+
reinterpret_cast<rnwgpu::ViewSnapshotCallbacks *>(callbackPointer));
|
|
48
|
+
if (!callbacks) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (error != nullptr) {
|
|
52
|
+
const char *chars = env->GetStringUTFChars(error, nullptr);
|
|
53
|
+
std::string message(chars ? chars : "drawElementImageToTexture failed");
|
|
54
|
+
if (chars) {
|
|
55
|
+
env->ReleaseStringUTFChars(error, chars);
|
|
56
|
+
}
|
|
57
|
+
callbacks->onError(std::move(message));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (bitmap == nullptr) {
|
|
61
|
+
callbacks->onError("drawElementImageToTexture: no bitmap was produced");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
AndroidBitmapInfo info;
|
|
65
|
+
if (AndroidBitmap_getInfo(env, bitmap, &info) !=
|
|
66
|
+
ANDROID_BITMAP_RESULT_SUCCESS ||
|
|
67
|
+
info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
|
|
68
|
+
callbacks->onError(
|
|
69
|
+
"drawElementImageToTexture: unexpected snapshot bitmap format");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
void *pixels = nullptr;
|
|
73
|
+
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) !=
|
|
74
|
+
ANDROID_BITMAP_RESULT_SUCCESS ||
|
|
75
|
+
pixels == nullptr) {
|
|
76
|
+
callbacks->onError(
|
|
77
|
+
"drawElementImageToTexture: couldn't lock the snapshot bitmap");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
rnwgpu::ImageData image;
|
|
81
|
+
image.width = info.width;
|
|
82
|
+
image.height = info.height;
|
|
83
|
+
image.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
84
|
+
// Bitmap.Config.ARGB_8888 stores premultiplied RGBA bytes.
|
|
85
|
+
image.premultiplied = true;
|
|
86
|
+
const size_t rowBytes = static_cast<size_t>(info.width) * 4;
|
|
87
|
+
image.data.resize(rowBytes * info.height);
|
|
88
|
+
const auto *src = static_cast<const uint8_t *>(pixels);
|
|
89
|
+
for (uint32_t row = 0; row < info.height; ++row) {
|
|
90
|
+
std::memcpy(image.data.data() + row * rowBytes, src + row * info.stride,
|
|
91
|
+
rowBytes);
|
|
92
|
+
}
|
|
93
|
+
AndroidBitmap_unlockPixels(env, bitmap);
|
|
94
|
+
callbacks->onSuccess(std::move(image));
|
|
95
|
+
}
|
|
96
|
+
|
|
37
97
|
extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceChanged(
|
|
38
98
|
JNIEnv *env, jobject thiz, jobject surface, jint contextId, jfloat width,
|
|
39
99
|
jfloat height) {
|