react-native-skia-webgpu-video 0.0.0-stage → 0.1.0-beta.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 +22 -0
- package/README.md +303 -2
- package/android/BUILD_VALIDATION.md +62 -0
- package/android/CMakeLists.txt +99 -0
- package/android/build-validation.json +262 -0
- package/android/build.gradle +185 -0
- package/android/cpp/EncoderTargets.cpp +226 -0
- package/android/cpp/EncoderTargets.h +70 -0
- package/android/cpp/JNIHelpers.cpp +15 -0
- package/android/cpp/JNIHelpers.h +20 -0
- package/android/cpp/NativeEventDispatcher.cpp +59 -0
- package/android/cpp/NativeEventDispatcher.h +31 -0
- package/android/cpp/NativeHardwareBuffer.cpp +397 -0
- package/android/cpp/NativeHardwareBuffer.h +34 -0
- package/android/cpp/NativeRgbaBuffer.cpp +45 -0
- package/android/cpp/NativeRgbaBuffer.h +9 -0
- package/android/cpp/ReturnedVideoFrames.h +37 -0
- package/android/cpp/RgbaFrameLease.h +53 -0
- package/android/cpp/VideoCapabilities.cpp +87 -0
- package/android/cpp/VideoCapabilities.h +44 -0
- package/android/cpp/VideoComposition.cpp +170 -0
- package/android/cpp/VideoComposition.h +36 -0
- package/android/cpp/VideoCompositionFramesExtractor.cpp +82 -0
- package/android/cpp/VideoCompositionFramesExtractor.h +48 -0
- package/android/cpp/VideoCompositionFramesExtractorHostObject.cpp +230 -0
- package/android/cpp/VideoCompositionFramesExtractorHostObject.h +42 -0
- package/android/cpp/VideoCompositionFramesExtractorSyncHostObject.cpp +133 -0
- package/android/cpp/VideoCompositionFramesExtractorSyncHostObject.h +46 -0
- package/android/cpp/VideoEncoderHostObject.cpp +318 -0
- package/android/cpp/VideoEncoderHostObject.h +76 -0
- package/android/cpp/VideoFrame.cpp +293 -0
- package/android/cpp/VideoFrame.h +27 -0
- package/android/cpp/VideoPlayer.cpp +96 -0
- package/android/cpp/VideoPlayer.h +52 -0
- package/android/cpp/VideoPlayerHostObject.cpp +225 -0
- package/android/cpp/VideoPlayerHostObject.h +43 -0
- package/android/cpp/cpp-adapter.cpp +316 -0
- package/android/gradle.properties +5 -0
- package/android/instrumentation-inventory.json +178 -0
- package/android/proguard-rules.pro +4 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/azzapp/rnskv/AudioCompositionExporter.java +537 -0
- package/android/src/main/java/com/azzapp/rnskv/AudioCompositionPlayer.java +321 -0
- package/android/src/main/java/com/azzapp/rnskv/BufferLifetime.java +55 -0
- package/android/src/main/java/com/azzapp/rnskv/DecodeRequests.java +100 -0
- package/android/src/main/java/com/azzapp/rnskv/DecoderWindow.java +55 -0
- package/android/src/main/java/com/azzapp/rnskv/EGLResourcesHolder.java +132 -0
- package/android/src/main/java/com/azzapp/rnskv/EGLUtils.java +225 -0
- package/android/src/main/java/com/azzapp/rnskv/FrameSize.java +50 -0
- package/android/src/main/java/com/azzapp/rnskv/GLFrameExtractor.java +510 -0
- package/android/src/main/java/com/azzapp/rnskv/JNIHelpers.java +12 -0
- package/android/src/main/java/com/azzapp/rnskv/NativeEventDispatcher.java +25 -0
- package/android/src/main/java/com/azzapp/rnskv/NativeHardwareBuffer.java +62 -0
- package/android/src/main/java/com/azzapp/rnskv/NativeRgbaBuffer.java +35 -0
- package/android/src/main/java/com/azzapp/rnskv/ReactNativeSkiaVideoModule.java +45 -0
- package/android/src/main/java/com/azzapp/rnskv/ReactNativeSkiaVideoPackage.java +43 -0
- package/android/src/main/java/com/azzapp/rnskv/ReactNativeSkiaVideoSpec.java +9 -0
- package/android/src/main/java/com/azzapp/rnskv/RgbaBufferAllocator.java +83 -0
- package/android/src/main/java/com/azzapp/rnskv/RgbaLayout.java +33 -0
- package/android/src/main/java/com/azzapp/rnskv/SurfaceOutputPolicy.java +13 -0
- package/android/src/main/java/com/azzapp/rnskv/TextureRenderer.java +150 -0
- package/android/src/main/java/com/azzapp/rnskv/TimeHelpers.java +11 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoCapabilities.java +354 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoComposition.java +147 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoCompositionDecoder.java +772 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoCompositionFramesExtractor.java +354 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoCompositionFramesExtractorSync.java +241 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoCompositionItemDecoder.java +492 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoEncoder.java +639 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoFrame.java +74 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoPlayer.java +361 -0
- package/cpp/RNSVCheckedSizes.h +54 -0
- package/cpp/RNSVEventEmitter.cpp +199 -0
- package/cpp/RNSVEventEmitter.h +31 -0
- package/cpp/RNSVHostObject.cpp +79 -0
- package/cpp/RNSVHostObject.h +67 -0
- package/cpp/RNSVMemoryBudget.cpp +68 -0
- package/cpp/RNSVMemoryBudget.h +100 -0
- package/cpp/RNSVRuntimeLifecycleMonitor.cpp +88 -0
- package/cpp/RNSVRuntimeLifecycleMonitor.h +33 -0
- package/docs/ANDROID_DEVICE_306.md +95 -0
- package/docs/ANDROID_EXPORT_CACHE_306.md +48 -0
- package/docs/ANDROID_MEMORY_RETENTION_306.md +67 -0
- package/docs/BENCHMARKS.md +368 -0
- package/docs/IOS_DEVICE_TESTS.md +115 -0
- package/docs/IOS_SIMULATOR_TESTS.md +74 -0
- package/docs/MIGRATION.md +82 -0
- package/docs/NATIVE_MEMORY_INVESTIGATION_306.md +88 -0
- package/docs/PERFORMANCE_AB_IPHONE_303.md +196 -0
- package/docs/PERFORMANCE_AB_SIMULATOR.md +135 -0
- package/docs/PERFORMANCE_AB_SOAK_SIMULATOR.md +84 -0
- package/docs/PERFORMANCE_IPHONE_306_OPTIMIZATION.md +76 -0
- package/docs/PERFORMANCE_SIMULATOR.md +89 -0
- package/docs/PERFORMANCE_SIMULATOR_INITIAL.md +96 -0
- package/docs/PERFORMANCE_SOAK_IPHONE_303.md +41 -0
- package/docs/UPSTREAM_NOTES.md +188 -0
- package/docs/assets/banner.svg +26 -0
- package/examples/webgpu-overlay.ts +147 -0
- package/ios/AVAssetTrackUtils.h +24 -0
- package/ios/AudioCompositionUtils.h +27 -0
- package/ios/AudioCompositionUtils.mm +113 -0
- package/ios/DecoderWindow.h +52 -0
- package/ios/NativeResourceStats.h +32 -0
- package/ios/RNSVColorSpace.h +50 -0
- package/ios/RNSVJSIUtils.h +46 -0
- package/ios/RNSVScopedPixelBuffer.h +17 -0
- package/ios/RNSVVideoPlayer.h +46 -0
- package/ios/RNSVVideoPlayer.mm +410 -0
- package/ios/ReactNativeSkiaVideo.h +8 -0
- package/ios/ReactNativeSkiaVideo.mm +310 -0
- package/ios/SeekPolicy.h +27 -0
- package/ios/VideoComposition.h +148 -0
- package/ios/VideoCompositionFramesExtractorHostObject.h +105 -0
- package/ios/VideoCompositionFramesExtractorHostObject.mm +686 -0
- package/ios/VideoCompositionFramesExtractorSyncHostObject.h +37 -0
- package/ios/VideoCompositionFramesExtractorSyncHostObject.mm +168 -0
- package/ios/VideoCompositionItemDecoder.h +60 -0
- package/ios/VideoCompositionItemDecoder.mm +480 -0
- package/ios/VideoEncoderHostObject.h +83 -0
- package/ios/VideoEncoderHostObject.mm +706 -0
- package/ios/VideoFrame.h +66 -0
- package/ios/VideoFrame.mm +108 -0
- package/ios/VideoPlayerHostObject.h +52 -0
- package/ios/VideoPlayerHostObject.mm +332 -0
- package/lib/module/NativeReactNativeSkiaVideo.js +5 -0
- package/lib/module/NativeReactNativeSkiaVideo.js.map +1 -0
- package/lib/module/RNSkiaVideoModule.js +20 -0
- package/lib/module/RNSkiaVideoModule.js.map +1 -0
- package/lib/module/canvas.js +18 -0
- package/lib/module/canvas.js.map +1 -0
- package/lib/module/exportCache.js +22 -0
- package/lib/module/exportCache.js.map +1 -0
- package/lib/module/exportFrames.js +338 -0
- package/lib/module/exportFrames.js.map +1 -0
- package/lib/module/exportQueue.js +52 -0
- package/lib/module/exportQueue.js.map +1 -0
- package/lib/module/exportTarget.js +137 -0
- package/lib/module/exportTarget.js.map +1 -0
- package/lib/module/exportVideoComposition.js +492 -0
- package/lib/module/exportVideoComposition.js.map +1 -0
- package/lib/module/frameInterop.js +532 -0
- package/lib/module/frameInterop.js.map +1 -0
- package/lib/module/gpu.js +222 -0
- package/lib/module/gpu.js.map +1 -0
- package/lib/module/gpuDevice.js +54 -0
- package/lib/module/gpuDevice.js.map +1 -0
- package/lib/module/index.js +36 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/memory.js +38 -0
- package/lib/module/memory.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/previewLifecycle.js +52 -0
- package/lib/module/previewLifecycle.js.map +1 -0
- package/lib/module/runtimeYield.js +10 -0
- package/lib/module/runtimeYield.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/useEventListener.js +18 -0
- package/lib/module/utils/useEventListener.js.map +1 -0
- package/lib/module/videoCompositionPlayer.js +348 -0
- package/lib/module/videoCompositionPlayer.js.map +1 -0
- package/lib/module/videoFrameImage.js +185 -0
- package/lib/module/videoFrameImage.js.map +1 -0
- package/lib/module/videoPlayer.js +172 -0
- package/lib/module/videoPlayer.js.map +1 -0
- package/lib/module/yuvBlit.js +299 -0
- package/lib/module/yuvBlit.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeReactNativeSkiaVideo.d.ts +7 -0
- package/lib/typescript/src/NativeReactNativeSkiaVideo.d.ts.map +1 -0
- package/lib/typescript/src/RNSkiaVideoModule.d.ts +4 -0
- package/lib/typescript/src/RNSkiaVideoModule.d.ts.map +1 -0
- package/lib/typescript/src/canvas.d.ts +4 -0
- package/lib/typescript/src/canvas.d.ts.map +1 -0
- package/lib/typescript/src/exportCache.d.ts +6 -0
- package/lib/typescript/src/exportCache.d.ts.map +1 -0
- package/lib/typescript/src/exportFrames.d.ts +57 -0
- package/lib/typescript/src/exportFrames.d.ts.map +1 -0
- package/lib/typescript/src/exportQueue.d.ts +5 -0
- package/lib/typescript/src/exportQueue.d.ts.map +1 -0
- package/lib/typescript/src/exportTarget.d.ts +28 -0
- package/lib/typescript/src/exportTarget.d.ts.map +1 -0
- package/lib/typescript/src/exportVideoComposition.d.ts +68 -0
- package/lib/typescript/src/exportVideoComposition.d.ts.map +1 -0
- package/lib/typescript/src/frameInterop.d.ts +17 -0
- package/lib/typescript/src/frameInterop.d.ts.map +1 -0
- package/lib/typescript/src/gpu.d.ts +39 -0
- package/lib/typescript/src/gpu.d.ts.map +1 -0
- package/lib/typescript/src/gpuDevice.d.ts +11 -0
- package/lib/typescript/src/gpuDevice.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +21 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/memory.d.ts +25 -0
- package/lib/typescript/src/memory.d.ts.map +1 -0
- package/lib/typescript/src/previewLifecycle.d.ts +22 -0
- package/lib/typescript/src/previewLifecycle.d.ts.map +1 -0
- package/lib/typescript/src/runtimeYield.d.ts +4 -0
- package/lib/typescript/src/runtimeYield.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +631 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils/useEventListener.d.ts +5 -0
- package/lib/typescript/src/utils/useEventListener.d.ts.map +1 -0
- package/lib/typescript/src/videoCompositionPlayer.d.ts +85 -0
- package/lib/typescript/src/videoCompositionPlayer.d.ts.map +1 -0
- package/lib/typescript/src/videoFrameImage.d.ts +60 -0
- package/lib/typescript/src/videoFrameImage.d.ts.map +1 -0
- package/lib/typescript/src/videoPlayer.d.ts +99 -0
- package/lib/typescript/src/videoPlayer.d.ts.map +1 -0
- package/lib/typescript/src/yuvBlit.d.ts +50 -0
- package/lib/typescript/src/yuvBlit.d.ts.map +1 -0
- package/package.json +154 -4
- package/patches/react-native-skia+3.7.0.patch +502 -0
- package/react-native-skia-webgpu-video.podspec +35 -0
- package/scripts/apply-skia-canvas-patch.mjs +48 -0
- package/scripts/skia-canvas-patch.json +291 -0
- package/src/NativeReactNativeSkiaVideo.ts +8 -0
- package/src/RNSkiaVideoModule.ts +27 -0
- package/src/canvas.ts +22 -0
- package/src/exportCache.ts +27 -0
- package/src/exportFrames.ts +421 -0
- package/src/exportQueue.ts +65 -0
- package/src/exportTarget.ts +179 -0
- package/src/exportVideoComposition.ts +718 -0
- package/src/frameInterop.ts +684 -0
- package/src/gpu.ts +290 -0
- package/src/gpuDevice.ts +80 -0
- package/src/index.tsx +45 -0
- package/src/memory.ts +48 -0
- package/src/previewLifecycle.ts +60 -0
- package/src/runtimeYield.ts +6 -0
- package/src/types.ts +661 -0
- package/src/utils/useEventListener.ts +22 -0
- package/src/videoCompositionPlayer.ts +520 -0
- package/src/videoFrameImage.ts +205 -0
- package/src/videoPlayer.ts +318 -0
- package/src/yuvBlit.ts +331 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <jsi/jsi.h>
|
|
6
|
+
|
|
7
|
+
namespace RNSkiaVideo {
|
|
8
|
+
using namespace facebook;
|
|
9
|
+
using namespace jni;
|
|
10
|
+
|
|
11
|
+
struct DecoderInfo : JavaClass<DecoderInfo> {
|
|
12
|
+
public:
|
|
13
|
+
static constexpr auto kJavaDescriptor =
|
|
14
|
+
"Lcom/azzapp/rnskv/VideoCapabilities$DecoderInfo;";
|
|
15
|
+
jint getMaxInstances() const;
|
|
16
|
+
jint getMaxWidth() const;
|
|
17
|
+
jint getMaxHeight() const;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
struct EncoderInfo : JavaClass<EncoderInfo> {
|
|
21
|
+
public:
|
|
22
|
+
static constexpr auto kJavaDescriptor =
|
|
23
|
+
"Lcom/azzapp/rnskv/VideoCapabilities$EncoderInfo;";
|
|
24
|
+
std::string getEncoderName() const;
|
|
25
|
+
jboolean getHardwareAccelerated() const;
|
|
26
|
+
jint getWidth() const;
|
|
27
|
+
jint getHeight() const;
|
|
28
|
+
jint getFrameRate() const;
|
|
29
|
+
jint getBitrate() const;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
struct VideoCapabilities : JavaClass<VideoCapabilities> {
|
|
33
|
+
public:
|
|
34
|
+
static constexpr auto kJavaDescriptor =
|
|
35
|
+
"Lcom/azzapp/rnskv/VideoCapabilities;";
|
|
36
|
+
static jni::local_ref<DecoderInfo>
|
|
37
|
+
getDecodingCapabilitiesFor(std::string mimeType);
|
|
38
|
+
static jni::local_ref<JList<EncoderInfo>>
|
|
39
|
+
getValidEncoderConfigurations(int width, int height, int framerate,
|
|
40
|
+
int bitrate,
|
|
41
|
+
std::optional<std::string> codec);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#include "RNSVCheckedSizes.h"
|
|
2
|
+
#include <set>
|
|
3
|
+
#include "VideoComposition.h"
|
|
4
|
+
#include "NativeHardwareBuffer.h"
|
|
5
|
+
|
|
6
|
+
namespace RNSkiaVideo {
|
|
7
|
+
|
|
8
|
+
using namespace facebook::jni;
|
|
9
|
+
|
|
10
|
+
local_ref<VideoCompositionItem>
|
|
11
|
+
VideoCompositionItem::create(std::string& id, std::string& path,
|
|
12
|
+
double compositionStartTime, double startTime,
|
|
13
|
+
double duration) {
|
|
14
|
+
auto inst = newInstance();
|
|
15
|
+
auto cls = javaClassStatic();
|
|
16
|
+
inst->setFieldValue(cls->getField<JString>("id"), make_jstring(id).get());
|
|
17
|
+
inst->setFieldValue(cls->getField<JString>("path"), make_jstring(path).get());
|
|
18
|
+
inst->setFieldValue(cls->getField<jdouble>("compositionStartTime"),
|
|
19
|
+
compositionStartTime);
|
|
20
|
+
inst->setFieldValue(cls->getField<jdouble>("startTime"), startTime);
|
|
21
|
+
inst->setFieldValue(cls->getField<jdouble>("duration"), duration);
|
|
22
|
+
return inst;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
std::string VideoCompositionItem::getId() const {
|
|
26
|
+
static const auto getIdMethod = getClass()->getMethod<jstring()>("getId");
|
|
27
|
+
return getIdMethod(self())->toStdString();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
std::string VideoCompositionItem::getPath() const {
|
|
31
|
+
static const auto getPathMethod = getClass()->getMethod<jstring()>("getPath");
|
|
32
|
+
return getPathMethod(self())->toStdString();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
jdouble VideoCompositionItem::getCompositionStartTime() const {
|
|
36
|
+
static const auto getCompositionStartTimeMethod =
|
|
37
|
+
getClass()->getMethod<jdouble()>("getCompositionStartTime");
|
|
38
|
+
return getCompositionStartTimeMethod(self());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
jdouble VideoCompositionItem::getStartTime() const {
|
|
42
|
+
static const auto getStartTimeMethod =
|
|
43
|
+
getClass()->getMethod<jdouble()>("getStartTime");
|
|
44
|
+
return getStartTimeMethod(self());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
jdouble VideoCompositionItem::getDuration() const {
|
|
48
|
+
static const auto getDurationMethod =
|
|
49
|
+
getClass()->getMethod<jdouble()>("getDuration");
|
|
50
|
+
return getDurationMethod(self());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
local_ref<VideoComposition>
|
|
54
|
+
VideoComposition::create(jdouble duration,
|
|
55
|
+
alias_ref<JList<VideoCompositionItem>> items) {
|
|
56
|
+
return newInstance(duration, items);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
local_ref<VideoComposition>
|
|
60
|
+
VideoComposition::fromJSIObject(jsi::Runtime& runtime,
|
|
61
|
+
jsi::Object& jsComposition) {
|
|
62
|
+
auto duration = checkedMediaSeconds(jsComposition.getProperty(runtime, "duration").asNumber());
|
|
63
|
+
auto jsItems = jsComposition.getProperty(runtime, "items")
|
|
64
|
+
.asObject(runtime)
|
|
65
|
+
.asArray(runtime);
|
|
66
|
+
auto size = jsItems.size(runtime);
|
|
67
|
+
local_ref<JList<VideoCompositionItem>> items =
|
|
68
|
+
JArrayList<VideoCompositionItem>::create(size);
|
|
69
|
+
if (size > INT32_MAX) throw jsi::JSError(runtime, "Too many composition items");
|
|
70
|
+
std::set<std::string> ids;
|
|
71
|
+
for (size_t i = 0; i < size; i++) {
|
|
72
|
+
auto jsItem = jsItems.getValueAtIndex(runtime, i).asObject(runtime);
|
|
73
|
+
auto id = jsItem.getProperty(runtime, "id").asString(runtime).utf8(runtime);
|
|
74
|
+
if (id.empty() || !ids.insert(id).second) throw jsi::JSError(runtime, "Composition item IDs must be nonempty and unique");
|
|
75
|
+
auto path =
|
|
76
|
+
jsItem.getProperty(runtime, "path").asString(runtime).utf8(runtime);
|
|
77
|
+
auto compositionStartTime =
|
|
78
|
+
checkedMediaSeconds(jsItem.getProperty(runtime, "compositionStartTime").asNumber());
|
|
79
|
+
auto startTime = checkedMediaSeconds(jsItem.getProperty(runtime, "startTime").asNumber());
|
|
80
|
+
auto duration = checkedMediaSeconds(jsItem.getProperty(runtime, "duration").asNumber());
|
|
81
|
+
|
|
82
|
+
auto item = VideoCompositionItem::create(id, path, compositionStartTime,
|
|
83
|
+
startTime, duration);
|
|
84
|
+
auto itemCls = VideoCompositionItem::javaClassStatic();
|
|
85
|
+
if (jsItem.hasProperty(runtime, "resolution")) {
|
|
86
|
+
auto resProp = jsItem.getProperty(runtime, "resolution");
|
|
87
|
+
if (resProp.isObject()) {
|
|
88
|
+
auto res = resProp.asObject(runtime);
|
|
89
|
+
item->setFieldValue(itemCls->getField<jint>("width"),
|
|
90
|
+
checkedPositiveInt(res.getProperty(runtime, "width").asNumber(), "decode width"));
|
|
91
|
+
item->setFieldValue(itemCls->getField<jint>("height"),
|
|
92
|
+
checkedPositiveInt(res.getProperty(runtime, "height").asNumber(), "decode height"));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (jsItem.hasProperty(runtime, "textureMode")) {
|
|
96
|
+
auto modeProp = jsItem.getProperty(runtime, "textureMode");
|
|
97
|
+
if (modeProp.isString() &&
|
|
98
|
+
modeProp.asString(runtime).utf8(runtime) == "direct") {
|
|
99
|
+
item->setFieldValue(itemCls->getField<jboolean>("directTexture"),
|
|
100
|
+
(jboolean)JNI_TRUE);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (jsItem.hasProperty(runtime, "maxLongSide")) {
|
|
104
|
+
auto capProp = jsItem.getProperty(runtime, "maxLongSide");
|
|
105
|
+
if (capProp.isNumber() && capProp.asNumber() > 0) {
|
|
106
|
+
item->setFieldValue(itemCls->getField<jint>("maxLongSide"),
|
|
107
|
+
checkedPositiveInt(capProp.asNumber(), "decode maxLongSide"));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
bool isVideo = true;
|
|
111
|
+
if (jsItem.hasProperty(runtime, "kind")) {
|
|
112
|
+
auto kindProp = jsItem.getProperty(runtime, "kind");
|
|
113
|
+
if (kindProp.isString()) {
|
|
114
|
+
isVideo = kindProp.asString(runtime).utf8(runtime) != "audio";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
bool audioEnabled = false;
|
|
118
|
+
double audioVolume = 1.0;
|
|
119
|
+
if (!isVideo) {
|
|
120
|
+
audioEnabled = true;
|
|
121
|
+
if (jsItem.hasProperty(runtime, "volume")) {
|
|
122
|
+
auto volumeProp = jsItem.getProperty(runtime, "volume");
|
|
123
|
+
if (volumeProp.isNumber()) {
|
|
124
|
+
audioVolume = volumeProp.asNumber();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} else if (jsItem.hasProperty(runtime, "audio")) {
|
|
128
|
+
auto audioProp = jsItem.getProperty(runtime, "audio");
|
|
129
|
+
if (audioProp.isBool()) {
|
|
130
|
+
audioEnabled = audioProp.getBool();
|
|
131
|
+
} else if (audioProp.isObject()) {
|
|
132
|
+
audioEnabled = true;
|
|
133
|
+
auto audio = audioProp.asObject(runtime);
|
|
134
|
+
if (audio.hasProperty(runtime, "volume")) {
|
|
135
|
+
auto volumeProp = audio.getProperty(runtime, "volume");
|
|
136
|
+
if (volumeProp.isNumber()) {
|
|
137
|
+
audioVolume = volumeProp.asNumber();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
item->setFieldValue(itemCls->getField<jboolean>("isVideo"),
|
|
143
|
+
(jboolean)isVideo);
|
|
144
|
+
item->setFieldValue(itemCls->getField<jboolean>("audioEnabled"),
|
|
145
|
+
(jboolean)audioEnabled);
|
|
146
|
+
item->setFieldValue(itemCls->getField<jdouble>("audioVolume"), audioVolume);
|
|
147
|
+
|
|
148
|
+
items->add(item);
|
|
149
|
+
}
|
|
150
|
+
auto composition = VideoComposition::create(duration, items);
|
|
151
|
+
composition->setFieldValue(VideoComposition::javaClassStatic()->getField<jboolean>(
|
|
152
|
+
"hardwareBufferEnabled"), static_cast<jboolean>(NativeHardwareBuffer::isEnabled()));
|
|
153
|
+
if (jsComposition.hasProperty(runtime, "lazyDecoders")) {
|
|
154
|
+
auto lazyProp = jsComposition.getProperty(runtime, "lazyDecoders");
|
|
155
|
+
if (lazyProp.isBool() && lazyProp.getBool()) {
|
|
156
|
+
composition->setFieldValue(
|
|
157
|
+
VideoComposition::javaClassStatic()->getField<jboolean>(
|
|
158
|
+
"lazyDecoders"),
|
|
159
|
+
(jboolean)JNI_TRUE);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return composition;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
bool VideoComposition::hasDirectTextures() const {
|
|
166
|
+
static const auto hasDirectTexturesMethod =
|
|
167
|
+
getClass()->getMethod<jboolean()>("hasDirectTextures");
|
|
168
|
+
return hasDirectTexturesMethod(self());
|
|
169
|
+
}
|
|
170
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <fbjni/ByteBuffer.h>
|
|
4
|
+
#include <fbjni/fbjni.h>
|
|
5
|
+
#include <jni.h>
|
|
6
|
+
#include <jsi/jsi.h>
|
|
7
|
+
|
|
8
|
+
namespace RNSkiaVideo {
|
|
9
|
+
|
|
10
|
+
using namespace facebook;
|
|
11
|
+
using namespace jni;
|
|
12
|
+
|
|
13
|
+
struct VideoCompositionItem : public JavaClass<VideoCompositionItem> {
|
|
14
|
+
static constexpr auto kJavaDescriptor =
|
|
15
|
+
"Lcom/azzapp/rnskv/VideoComposition$Item;";
|
|
16
|
+
static local_ref<VideoCompositionItem>
|
|
17
|
+
create(std::string& id, std::string& path, double compositionStartTime,
|
|
18
|
+
double startTime, double duration);
|
|
19
|
+
|
|
20
|
+
std::string getId() const;
|
|
21
|
+
std::string getPath() const;
|
|
22
|
+
double getCompositionStartTime() const;
|
|
23
|
+
double getStartTime() const;
|
|
24
|
+
double getDuration() const;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
struct VideoComposition : public JavaClass<VideoComposition> {
|
|
28
|
+
static constexpr auto kJavaDescriptor = "Lcom/azzapp/rnskv/VideoComposition;";
|
|
29
|
+
static local_ref<VideoComposition>
|
|
30
|
+
create(double duration, alias_ref<JList<VideoCompositionItem>> items);
|
|
31
|
+
static local_ref<VideoComposition> fromJSIObject(jsi::Runtime& runtime,
|
|
32
|
+
jsi::Object& jsComposition);
|
|
33
|
+
/** Whether an item hands its decoder's buffers over (textureMode: direct). */
|
|
34
|
+
bool hasDirectTextures() const;
|
|
35
|
+
};
|
|
36
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#include "VideoCompositionFramesExtractor.h"
|
|
2
|
+
|
|
3
|
+
namespace RNSkiaVideo {
|
|
4
|
+
using namespace facebook::jni;
|
|
5
|
+
|
|
6
|
+
local_ref<VideoCompositionFramesExtractor>
|
|
7
|
+
VideoCompositionFramesExtractor::create(
|
|
8
|
+
alias_ref<VideoComposition> composition,
|
|
9
|
+
alias_ref<NativeEventDispatcher> dispatcher) {
|
|
10
|
+
return newInstance(composition, dispatcher);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
void VideoCompositionFramesExtractor::prepare() const {
|
|
14
|
+
static const auto prepareMethod = getClass()->getMethod<void()>("prepare");
|
|
15
|
+
prepareMethod(self());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
void VideoCompositionFramesExtractor::play() const {
|
|
19
|
+
static const auto playMethod = getClass()->getMethod<void()>("play");
|
|
20
|
+
playMethod(self());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
void VideoCompositionFramesExtractor::pause() const {
|
|
24
|
+
static const auto pauseMethod = getClass()->getMethod<void()>("pause");
|
|
25
|
+
pauseMethod(self());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void VideoCompositionFramesExtractor::seekTo(jlong position) const {
|
|
29
|
+
static const auto seekToMethod = getClass()->getMethod<void(jlong)>("seekTo");
|
|
30
|
+
seekToMethod(self(), position);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
jboolean VideoCompositionFramesExtractor::getIsPlaying() const {
|
|
34
|
+
static const auto getIsPlayingMethod =
|
|
35
|
+
getClass()->getMethod<jboolean()>("getIsPlaying");
|
|
36
|
+
return getIsPlayingMethod(self());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
jboolean VideoCompositionFramesExtractor::getIsLooping() const {
|
|
40
|
+
static const auto getIsLoopingMethod =
|
|
41
|
+
getClass()->getMethod<jboolean()>("getIsLooping");
|
|
42
|
+
return getIsLoopingMethod(self());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void VideoCompositionFramesExtractor::setIsLooping(jboolean isLooping) const {
|
|
46
|
+
static const auto setIsLoopingMethod =
|
|
47
|
+
getClass()->getMethod<void(jboolean)>("setIsLooping");
|
|
48
|
+
setIsLoopingMethod(self(), isLooping);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
jlong VideoCompositionFramesExtractor::getCurrentPosition() const {
|
|
52
|
+
static const auto getCurrentPositionMethod =
|
|
53
|
+
getClass()->getMethod<jlong()>("getCurrentPosition");
|
|
54
|
+
return getCurrentPositionMethod(self());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
jlong VideoCompositionFramesExtractor::getFramesVersion() const {
|
|
58
|
+
static const auto getFramesVersionMethod =
|
|
59
|
+
getClass()->getMethod<jlong()>("getFramesVersion");
|
|
60
|
+
return getFramesVersionMethod(self());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void VideoCompositionFramesExtractor::makeGLContextCurrent() const {
|
|
64
|
+
static const auto makeGLContextCurrentMethod =
|
|
65
|
+
getClass()->getMethod<void()>("makeGLContextCurrent");
|
|
66
|
+
makeGLContextCurrentMethod(self());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
local_ref<JMap<JString, VideoFrame>>
|
|
70
|
+
VideoCompositionFramesExtractor::decodeCompositionFrames() {
|
|
71
|
+
static const auto decodeCompositionFramesMethod =
|
|
72
|
+
getClass()->getMethod<JMap<JString, VideoFrame>()>(
|
|
73
|
+
"decodeCompositionFrames");
|
|
74
|
+
return decodeCompositionFramesMethod(self());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
void VideoCompositionFramesExtractor::release() const {
|
|
78
|
+
static const auto releaseMethod = getClass()->getMethod<void()>("release");
|
|
79
|
+
releaseMethod(self());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
|
|
5
|
+
#include "NativeEventDispatcher.h"
|
|
6
|
+
#include "VideoComposition.h"
|
|
7
|
+
#include "VideoFrame.h"
|
|
8
|
+
|
|
9
|
+
namespace RNSkiaVideo {
|
|
10
|
+
|
|
11
|
+
using namespace facebook;
|
|
12
|
+
using namespace jni;
|
|
13
|
+
|
|
14
|
+
struct VideoCompositionFramesExtractor
|
|
15
|
+
: public jni::JavaClass<VideoCompositionFramesExtractor> {
|
|
16
|
+
|
|
17
|
+
public:
|
|
18
|
+
static constexpr auto kJavaDescriptor =
|
|
19
|
+
"Lcom/azzapp/rnskv/VideoCompositionFramesExtractor;";
|
|
20
|
+
|
|
21
|
+
local_ref<VideoCompositionFramesExtractor> static create(
|
|
22
|
+
alias_ref<VideoComposition> composition,
|
|
23
|
+
alias_ref<RNSkiaVideo::NativeEventDispatcher> dispatcher);
|
|
24
|
+
|
|
25
|
+
void prepare() const;
|
|
26
|
+
|
|
27
|
+
void play() const;
|
|
28
|
+
|
|
29
|
+
void pause() const;
|
|
30
|
+
|
|
31
|
+
void seekTo(jlong position) const;
|
|
32
|
+
|
|
33
|
+
jboolean getIsPlaying() const;
|
|
34
|
+
|
|
35
|
+
jlong getCurrentPosition() const;
|
|
36
|
+
|
|
37
|
+
jlong getFramesVersion() const;
|
|
38
|
+
|
|
39
|
+
jboolean getIsLooping() const;
|
|
40
|
+
|
|
41
|
+
void setIsLooping(jboolean isLooping) const;
|
|
42
|
+
|
|
43
|
+
local_ref<JMap<JString, VideoFrame>> decodeCompositionFrames();
|
|
44
|
+
|
|
45
|
+
void makeGLContextCurrent() const;
|
|
46
|
+
void release() const;
|
|
47
|
+
};
|
|
48
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#include "RNSVCheckedSizes.h"
|
|
2
|
+
#include "VideoCompositionFramesExtractorHostObject.h"
|
|
3
|
+
#include "ReturnedVideoFrames.h"
|
|
4
|
+
#include "JNIHelpers.h"
|
|
5
|
+
|
|
6
|
+
namespace RNSkiaVideo {
|
|
7
|
+
|
|
8
|
+
VideoCompositionFramesExtractorHostObject::
|
|
9
|
+
VideoCompositionFramesExtractorHostObject(jsi::Runtime& runtime,
|
|
10
|
+
jsi::Object jsComposition)
|
|
11
|
+
: EventEmitter(runtime, JNIHelpers::getCallInvoker()) {}
|
|
12
|
+
|
|
13
|
+
void VideoCompositionFramesExtractorHostObject::initialize(
|
|
14
|
+
jsi::Runtime& runtime, jsi::Object jsComposition) {
|
|
15
|
+
auto receiver = std::shared_ptr<JEventReceiver>(shared_from_this(), this);
|
|
16
|
+
jEventDispatcher = make_global(NativeEventDispatcher::create(receiver));
|
|
17
|
+
auto composition = VideoComposition::fromJSIObject(runtime, jsComposition);
|
|
18
|
+
player = make_global(
|
|
19
|
+
VideoCompositionFramesExtractor::create(composition, jEventDispatcher));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
VideoCompositionFramesExtractorHostObject::
|
|
23
|
+
~VideoCompositionFramesExtractorHostObject() {
|
|
24
|
+
this->release();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
std::vector<jsi::PropNameID>
|
|
28
|
+
VideoCompositionFramesExtractorHostObject::getPropertyNames(jsi::Runtime& rt) {
|
|
29
|
+
std::vector<jsi::PropNameID> result;
|
|
30
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("prepare")));
|
|
31
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("play")));
|
|
32
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("pause")));
|
|
33
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("seekTo")));
|
|
34
|
+
result.push_back(
|
|
35
|
+
jsi::PropNameID::forUtf8(rt, std::string("decodeCompositionFrames")));
|
|
36
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("on")));
|
|
37
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("dispose")));
|
|
38
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("currentTime")));
|
|
39
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("framesVersion")));
|
|
40
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isLooping")));
|
|
41
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isPlaying")));
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The methods are created once per runtime (see RNSVHostObject):
|
|
46
|
+
// `decodeCompositionFrames` is read at every vsync of the UI runtime.
|
|
47
|
+
jsi::Value VideoCompositionFramesExtractorHostObject::get(
|
|
48
|
+
jsi::Runtime& runtime, const jsi::PropNameID& propNameId) {
|
|
49
|
+
auto propName = propNameId.utf8(runtime);
|
|
50
|
+
if (propName == "prepare") {
|
|
51
|
+
return getFunction(
|
|
52
|
+
runtime, propName, 0,
|
|
53
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
54
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
55
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
56
|
+
if (!released.test() && !prepared.test_and_set()) {
|
|
57
|
+
player->prepare();
|
|
58
|
+
}
|
|
59
|
+
return jsi::Value::undefined();
|
|
60
|
+
});
|
|
61
|
+
} else if (propName == "decodeCompositionFrames") {
|
|
62
|
+
return getFunction(
|
|
63
|
+
runtime, propName, 0,
|
|
64
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
65
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
66
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
67
|
+
if (released.test() || !prepared.test()) {
|
|
68
|
+
return jsi::Object(runtime);
|
|
69
|
+
}
|
|
70
|
+
auto frames = player->decodeCompositionFrames();
|
|
71
|
+
ReturnedVideoFrames returned(frames);
|
|
72
|
+
auto version = player->getFramesVersion();
|
|
73
|
+
bool hardware = false;
|
|
74
|
+
for (auto& entry : *frames) {
|
|
75
|
+
auto frame = entry.second;
|
|
76
|
+
if (frame->isHardwareBuffer()) { hardware = true; break; }
|
|
77
|
+
}
|
|
78
|
+
if (hardware) {
|
|
79
|
+
// A previous JS frame may already have been disposed after copying
|
|
80
|
+
// into its owned Skia image. New leases keep paused rewrapping valid.
|
|
81
|
+
auto result = jsi::Object(runtime);
|
|
82
|
+
for (auto& entry : *frames) {
|
|
83
|
+
auto frame = entry.second;
|
|
84
|
+
result.setProperty(runtime, entry.first->toStdString().c_str(), frame->toJS(runtime));
|
|
85
|
+
}
|
|
86
|
+
returned.close();
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
// The frames object is only rebuilt when the decoder produced a
|
|
90
|
+
// new frame. On a 120 Hz display most calls see the same frames as
|
|
91
|
+
// the previous one, and rewrapping them would be several JNI calls
|
|
92
|
+
// and JS allocations per item per vsync.
|
|
93
|
+
auto result = getVersionedObject(
|
|
94
|
+
runtime, "frames", (double)version,
|
|
95
|
+
[&](jsi::Object& result) {
|
|
96
|
+
for (auto& entry : *frames) {
|
|
97
|
+
auto id = entry.first->toStdString();
|
|
98
|
+
auto frame = entry.second;
|
|
99
|
+
result.setProperty(runtime, id.c_str(), frame->toJS(runtime));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
returned.close();
|
|
103
|
+
return result;
|
|
104
|
+
});
|
|
105
|
+
} else if (propName == "play") {
|
|
106
|
+
return getFunction(
|
|
107
|
+
runtime, propName, 0,
|
|
108
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
109
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
110
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
111
|
+
if (!released.test()) {
|
|
112
|
+
player->play();
|
|
113
|
+
}
|
|
114
|
+
return jsi::Value::undefined();
|
|
115
|
+
});
|
|
116
|
+
} else if (propName == "pause") {
|
|
117
|
+
return getFunction(
|
|
118
|
+
runtime, propName, 0,
|
|
119
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
120
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
121
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
122
|
+
if (!released.test()) {
|
|
123
|
+
player->pause();
|
|
124
|
+
}
|
|
125
|
+
return jsi::Value::undefined();
|
|
126
|
+
});
|
|
127
|
+
} else if (propName == "seekTo") {
|
|
128
|
+
return getFunction(
|
|
129
|
+
runtime, propName, 1,
|
|
130
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
131
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
132
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
133
|
+
if (!released.test()) {
|
|
134
|
+
if (count != 1) {
|
|
135
|
+
throw jsi::JSError(
|
|
136
|
+
runtime,
|
|
137
|
+
"VideoCompositionFramesExtractorHostObject."
|
|
138
|
+
"seekTo(..) expects 1 arguments (number)!");
|
|
139
|
+
}
|
|
140
|
+
auto posSec = checkedMediaSeconds(arguments[0].asNumber());
|
|
141
|
+
player->seekTo((long)(posSec * 1000000));
|
|
142
|
+
}
|
|
143
|
+
return jsi::Value::undefined();
|
|
144
|
+
});
|
|
145
|
+
} else if (propName == "on") {
|
|
146
|
+
return getFunction(
|
|
147
|
+
runtime, propName, 2,
|
|
148
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
149
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
150
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
151
|
+
if (released.test()) {
|
|
152
|
+
// Nothing to listen to anymore: hand out a no-op unsubscribe.
|
|
153
|
+
return jsi::Function::createFromHostFunction(
|
|
154
|
+
runtime, jsi::PropNameID::forAscii(runtime, "dispose"), 0,
|
|
155
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
156
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
157
|
+
return jsi::Value::undefined();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
auto name = arguments[0].asString(runtime).utf8(runtime);
|
|
161
|
+
auto handler = arguments[1].asObject(runtime).asFunction(runtime);
|
|
162
|
+
return this->on(name, std::move(handler));
|
|
163
|
+
});
|
|
164
|
+
} else if (propName == "dispose") {
|
|
165
|
+
return getFunction(
|
|
166
|
+
runtime, propName, 0,
|
|
167
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
168
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
169
|
+
this->release();
|
|
170
|
+
return jsi::Value::undefined();
|
|
171
|
+
});
|
|
172
|
+
} else if (propName == "currentTime") {
|
|
173
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
174
|
+
return {released.test() ? 0
|
|
175
|
+
: (double)player->getCurrentPosition() / 1000000.0};
|
|
176
|
+
} else if (propName == "framesVersion") {
|
|
177
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
178
|
+
return {released.test() || !prepared.test()
|
|
179
|
+
? 0
|
|
180
|
+
: (double)player->getFramesVersion()};
|
|
181
|
+
} else if (propName == "isLooping") {
|
|
182
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
183
|
+
return {!released.test() && player->getIsLooping()};
|
|
184
|
+
} else if (propName == "isPlaying") {
|
|
185
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
186
|
+
return {!released.test() && player->getIsPlaying()};
|
|
187
|
+
}
|
|
188
|
+
return jsi::Value::undefined();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
void VideoCompositionFramesExtractorHostObject::set(
|
|
192
|
+
facebook::jsi::Runtime& runtime,
|
|
193
|
+
const facebook::jsi::PropNameID& propNameId,
|
|
194
|
+
const facebook::jsi::Value& value) {
|
|
195
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
196
|
+
if (released.test()) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
auto propName = propNameId.utf8(runtime);
|
|
200
|
+
if (propName == "isLooping") {
|
|
201
|
+
player->setIsLooping(value.asBool());
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
void VideoCompositionFramesExtractorHostObject::handleEvent(
|
|
206
|
+
std::string eventName, alias_ref<jobject> data) {
|
|
207
|
+
if (eventName == "error") {
|
|
208
|
+
auto message = static_ref_cast<JString>(data)->toStdString();
|
|
209
|
+
emit("error", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
210
|
+
auto dimensions = jsi::Object(runtime);
|
|
211
|
+
dimensions.setProperty(runtime, "message",
|
|
212
|
+
jsi::String::createFromUtf8(runtime, message));
|
|
213
|
+
return dimensions;
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
emit(eventName);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void VideoCompositionFramesExtractorHostObject::release() {
|
|
221
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
222
|
+
if (!released.test_and_set()) {
|
|
223
|
+
if (jEventDispatcher) jEventDispatcher->invalidate();
|
|
224
|
+
removeAllListeners();
|
|
225
|
+
if (player) player->release();
|
|
226
|
+
player = nullptr;
|
|
227
|
+
jEventDispatcher = nullptr;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <mutex>
|
|
4
|
+
|
|
5
|
+
#include "NativeEventDispatcher.h"
|
|
6
|
+
#include "RNSVHostObject.h"
|
|
7
|
+
#include "VideoCompositionFramesExtractor.h"
|
|
8
|
+
#include <fbjni/fbjni.h>
|
|
9
|
+
#include <jsi/jsi.h>
|
|
10
|
+
#include <map>
|
|
11
|
+
|
|
12
|
+
using namespace facebook;
|
|
13
|
+
|
|
14
|
+
namespace RNSkiaVideo {
|
|
15
|
+
|
|
16
|
+
class JSI_EXPORT VideoCompositionFramesExtractorHostObject
|
|
17
|
+
: public RNSVHostObject,
|
|
18
|
+
EventEmitter,
|
|
19
|
+
JEventReceiver {
|
|
20
|
+
public:
|
|
21
|
+
VideoCompositionFramesExtractorHostObject(jsi::Runtime& runtime, jsi::Object);
|
|
22
|
+
void initialize(jsi::Runtime& runtime, jsi::Object composition);
|
|
23
|
+
~VideoCompositionFramesExtractorHostObject() override;
|
|
24
|
+
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
|
|
25
|
+
void set(jsi::Runtime&, const jsi::PropNameID& name,
|
|
26
|
+
const jsi::Value& value) override;
|
|
27
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
|
|
28
|
+
void handleEvent(std::string eventName, alias_ref<jobject> data) override;
|
|
29
|
+
|
|
30
|
+
private:
|
|
31
|
+
global_ref<NativeEventDispatcher> jEventDispatcher;
|
|
32
|
+
global_ref<VideoCompositionFramesExtractor> player;
|
|
33
|
+
std::atomic_flag prepared = ATOMIC_FLAG_INIT;
|
|
34
|
+
std::atomic_flag released = ATOMIC_FLAG_INIT;
|
|
35
|
+
// Held by every use of `player` and by release(): the UI thread draws
|
|
36
|
+
// while the JS thread disposes, and a use that passed the released check
|
|
37
|
+
// went on with a player release() had just reset.
|
|
38
|
+
std::recursive_mutex playerMutex;
|
|
39
|
+
void release();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
} // namespace RNSkiaVideo
|