react-native-skia-webgpu-video 0.0.0-stage → 0.1.0-beta.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/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/DisplayVsyncPeriod.java +72 -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 +43 -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 +114 -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 +779 -0
- package/android/src/main/java/com/azzapp/rnskv/VideoCompositionFramesExtractor.java +463 -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 +368 -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 +105 -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 +155 -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 +568 -0
- package/src/videoFrameImage.ts +205 -0
- package/src/videoPlayer.ts +318 -0
- package/src/yuvBlit.ts +331 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#include "RNSVCheckedSizes.h"
|
|
2
|
+
#include "VideoPlayerHostObject.h"
|
|
3
|
+
#include "JNIHelpers.h"
|
|
4
|
+
|
|
5
|
+
namespace RNSkiaVideo {
|
|
6
|
+
VideoPlayerHostObject::VideoPlayerHostObject(jsi::Runtime& runtime,
|
|
7
|
+
const std::string& uri, int width,
|
|
8
|
+
int height)
|
|
9
|
+
: EventEmitter(runtime, JNIHelpers::getCallInvoker()) {}
|
|
10
|
+
|
|
11
|
+
void VideoPlayerHostObject::initialize(const std::string& uri, int width, int height) {
|
|
12
|
+
auto receiver = std::shared_ptr<JEventReceiver>(shared_from_this(), this);
|
|
13
|
+
jEventDispatcher = make_global(NativeEventDispatcher::create(receiver));
|
|
14
|
+
player =
|
|
15
|
+
make_global(VideoPlayer::create(uri, width, height, jEventDispatcher));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
VideoPlayerHostObject::~VideoPlayerHostObject() {
|
|
19
|
+
this->release();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
std::vector<jsi::PropNameID>
|
|
23
|
+
VideoPlayerHostObject::getPropertyNames(jsi::Runtime& rt) {
|
|
24
|
+
std::vector<jsi::PropNameID> result;
|
|
25
|
+
result.push_back(
|
|
26
|
+
jsi::PropNameID::forUtf8(rt, std::string("decodeNextFrame")));
|
|
27
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("play")));
|
|
28
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("pause")));
|
|
29
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("seekTo")));
|
|
30
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("currentTime")));
|
|
31
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("duration")));
|
|
32
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("volume")));
|
|
33
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("playbackSpeed")));
|
|
34
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isLooping")));
|
|
35
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isPlaying")));
|
|
36
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("dispose")));
|
|
37
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("on")));
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The methods are created once per runtime (see RNSVHostObject):
|
|
42
|
+
// `decodeNextFrame` is read by useVideoPlayer at every vsync of the UI
|
|
43
|
+
// runtime, and a fresh host function on each read is two garbage collected
|
|
44
|
+
// allocations per frame for nothing.
|
|
45
|
+
jsi::Value VideoPlayerHostObject::get(jsi::Runtime& runtime,
|
|
46
|
+
const jsi::PropNameID& propNameId) {
|
|
47
|
+
auto propName = propNameId.utf8(runtime);
|
|
48
|
+
if (propName == "decodeNextFrame") {
|
|
49
|
+
return getFunction(
|
|
50
|
+
runtime, propName, 0,
|
|
51
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
52
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
53
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
54
|
+
if (released.test()) {
|
|
55
|
+
return jsi::Value::null();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!glPrepared) {
|
|
59
|
+
player->setupGL();
|
|
60
|
+
glPrepared = true;
|
|
61
|
+
}
|
|
62
|
+
auto frame = player->decodeNextFrame();
|
|
63
|
+
if (!frame) return jsi::Value::null();
|
|
64
|
+
return frame->toJS(runtime);
|
|
65
|
+
});
|
|
66
|
+
} else if (propName == "play") {
|
|
67
|
+
return getFunction(
|
|
68
|
+
runtime, propName, 0,
|
|
69
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
70
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
71
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
72
|
+
if (!released.test()) {
|
|
73
|
+
player->play();
|
|
74
|
+
}
|
|
75
|
+
return jsi::Value::undefined();
|
|
76
|
+
});
|
|
77
|
+
} else if (propName == "pause") {
|
|
78
|
+
return getFunction(
|
|
79
|
+
runtime, propName, 0,
|
|
80
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
81
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
82
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
83
|
+
if (!released.test()) {
|
|
84
|
+
player->pause();
|
|
85
|
+
}
|
|
86
|
+
return jsi::Value::undefined();
|
|
87
|
+
});
|
|
88
|
+
} else if (propName == "seekTo") {
|
|
89
|
+
return getFunction(
|
|
90
|
+
runtime, propName, 1,
|
|
91
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
92
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
93
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
94
|
+
if (released.test()) {
|
|
95
|
+
return jsi::Value::undefined();
|
|
96
|
+
}
|
|
97
|
+
if (count < 1 || !arguments[0].isNumber())
|
|
98
|
+
throw jsi::JSError(runtime, "seekTo expects media seconds");
|
|
99
|
+
auto time = checkedMediaSeconds(arguments[0].asNumber());
|
|
100
|
+
player->seekTo(time * 1000);
|
|
101
|
+
return jsi::Value::undefined();
|
|
102
|
+
});
|
|
103
|
+
} else if (propName == "currentTime") {
|
|
104
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
105
|
+
return jsi::Value(
|
|
106
|
+
released.test() ? 0 : (double)player->getCurrentPosition() / 1000.0);
|
|
107
|
+
} else if (propName == "duration") {
|
|
108
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
109
|
+
return jsi::Value(released.test() ? 0
|
|
110
|
+
: (double)player->getDuration() / 1000.0);
|
|
111
|
+
} else if (propName == "volume") {
|
|
112
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
113
|
+
return jsi::Value(released.test() ? 0 : (double)player->getVolume());
|
|
114
|
+
} else if (propName == "playbackSpeed") {
|
|
115
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
116
|
+
return jsi::Value(released.test() ? 1 : (double)player->getPlaybackSpeed());
|
|
117
|
+
} else if (propName == "isLooping") {
|
|
118
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
119
|
+
return jsi::Value(!(released.test()) && player->getIsLooping());
|
|
120
|
+
} else if (propName == "isPlaying") {
|
|
121
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
122
|
+
return jsi::Value(!(released.test()) && player->getIsPlaying());
|
|
123
|
+
} else if (propName == "on") {
|
|
124
|
+
return getFunction(
|
|
125
|
+
runtime, propName, 2,
|
|
126
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
127
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
128
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
129
|
+
if (released.test()) {
|
|
130
|
+
// Nothing to listen to anymore: hand out a no-op unsubscribe.
|
|
131
|
+
return jsi::Function::createFromHostFunction(
|
|
132
|
+
runtime, jsi::PropNameID::forAscii(runtime, "dispose"), 0,
|
|
133
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
134
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
135
|
+
return jsi::Value::undefined();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
auto name = arguments[0].asString(runtime).utf8(runtime);
|
|
139
|
+
auto handler = arguments[1].asObject(runtime).asFunction(runtime);
|
|
140
|
+
return this->on(name, std::move(handler));
|
|
141
|
+
});
|
|
142
|
+
} else if (propName == "dispose") {
|
|
143
|
+
return getFunction(
|
|
144
|
+
runtime, propName, 0,
|
|
145
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
146
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
147
|
+
this->release();
|
|
148
|
+
return jsi::Value::undefined();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return jsi::Value::undefined();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
void VideoPlayerHostObject::set(facebook::jsi::Runtime& runtime,
|
|
155
|
+
const facebook::jsi::PropNameID& propNameId,
|
|
156
|
+
const facebook::jsi::Value& value) {
|
|
157
|
+
auto propName = propNameId.utf8(runtime);
|
|
158
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
159
|
+
if (released.test()) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (propName == "volume") {
|
|
163
|
+
player->setVolume(value.asNumber());
|
|
164
|
+
} else if (propName == "playbackSpeed") {
|
|
165
|
+
player->setPlaybackSpeed(value.asNumber());
|
|
166
|
+
} else if (propName == "isLooping") {
|
|
167
|
+
player->setIsLooping(value.asBool());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void VideoPlayerHostObject::handleEvent(std::string eventName,
|
|
172
|
+
alias_ref<jobject> data) {
|
|
173
|
+
if (eventName == "ready") {
|
|
174
|
+
auto dimensions = static_ref_cast<JArrayInt>(data)->getRegion(0, 3);
|
|
175
|
+
int width = dimensions[0];
|
|
176
|
+
int height = dimensions[1];
|
|
177
|
+
int rotation = dimensions[2];
|
|
178
|
+
emit("ready", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
179
|
+
auto dimensions = jsi::Object(runtime);
|
|
180
|
+
dimensions.setProperty(runtime, "width", jsi::Value(width));
|
|
181
|
+
dimensions.setProperty(runtime, "height", jsi::Value(height));
|
|
182
|
+
dimensions.setProperty(runtime, "rotation", jsi::Value(rotation));
|
|
183
|
+
return dimensions;
|
|
184
|
+
});
|
|
185
|
+
} else if (eventName == "error") {
|
|
186
|
+
auto message = static_ref_cast<JString>(data)->toStdString();
|
|
187
|
+
emit("error", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
188
|
+
auto dimensions = jsi::Object(runtime);
|
|
189
|
+
dimensions.setProperty(runtime, "message",
|
|
190
|
+
jsi::String::createFromUtf8(runtime, message));
|
|
191
|
+
return dimensions;
|
|
192
|
+
});
|
|
193
|
+
} else if (eventName == "bufferingUpdate") {
|
|
194
|
+
auto bufferedDuration = (double)static_ref_cast<JLong>(data)->value();
|
|
195
|
+
emit("bufferingUpdate", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
196
|
+
auto range = jsi::Object(runtime);
|
|
197
|
+
range.setProperty(runtime, "start", jsi::Value(0));
|
|
198
|
+
range.setProperty(runtime, "duration",
|
|
199
|
+
jsi::Value(bufferedDuration / 1000));
|
|
200
|
+
auto ranges = jsi::Array(runtime, 1);
|
|
201
|
+
ranges.setValueAtIndex(runtime, 0, range);
|
|
202
|
+
return ranges;
|
|
203
|
+
});
|
|
204
|
+
} else if (eventName == "playingStatusChange") {
|
|
205
|
+
bool playing = static_ref_cast<JBoolean>(data)->value();
|
|
206
|
+
__android_log_print(ANDROID_LOG_INFO, "VideoPlayer",
|
|
207
|
+
"playingStatusChange %d", playing);
|
|
208
|
+
emit("playingStatusChange",
|
|
209
|
+
[=](jsi::Runtime&) { return jsi::Value(playing); });
|
|
210
|
+
} else {
|
|
211
|
+
emit(eventName);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void VideoPlayerHostObject::release() {
|
|
216
|
+
std::lock_guard<std::recursive_mutex> lock(playerMutex);
|
|
217
|
+
if (!released.test_and_set()) {
|
|
218
|
+
if (jEventDispatcher) jEventDispatcher->invalidate();
|
|
219
|
+
if (player) player->release();
|
|
220
|
+
player = nullptr;
|
|
221
|
+
this->removeAllListeners();
|
|
222
|
+
jEventDispatcher = nullptr;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <mutex>
|
|
4
|
+
|
|
5
|
+
#include <fbjni/fbjni.h>
|
|
6
|
+
#include <jsi/jsi.h>
|
|
7
|
+
#include <map>
|
|
8
|
+
|
|
9
|
+
#include "RNSVEventEmitter.h"
|
|
10
|
+
#include "RNSVHostObject.h"
|
|
11
|
+
#include "VideoPlayer.h"
|
|
12
|
+
|
|
13
|
+
using namespace facebook;
|
|
14
|
+
|
|
15
|
+
namespace RNSkiaVideo {
|
|
16
|
+
|
|
17
|
+
class JSI_EXPORT VideoPlayerHostObject : public RNSVHostObject,
|
|
18
|
+
JEventReceiver,
|
|
19
|
+
EventEmitter {
|
|
20
|
+
public:
|
|
21
|
+
VideoPlayerHostObject(jsi::Runtime& runtime, const std::string& uri,
|
|
22
|
+
int width, int height);
|
|
23
|
+
void initialize(const std::string& uri, int width, int height);
|
|
24
|
+
~VideoPlayerHostObject();
|
|
25
|
+
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
|
|
26
|
+
void set(jsi::Runtime&, const jsi::PropNameID& name,
|
|
27
|
+
const jsi::Value& value) override;
|
|
28
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
|
|
29
|
+
void handleEvent(std::string eventName, alias_ref<jobject> data) override;
|
|
30
|
+
|
|
31
|
+
private:
|
|
32
|
+
global_ref<NativeEventDispatcher> jEventDispatcher;
|
|
33
|
+
jni::global_ref<VideoPlayer> player;
|
|
34
|
+
bool glPrepared = false;
|
|
35
|
+
std::atomic_flag released = ATOMIC_FLAG_INIT;
|
|
36
|
+
// Held by every use of `player` and by release(): the UI thread draws
|
|
37
|
+
// while the JS thread disposes, and a use that passed the released check
|
|
38
|
+
// went on with a player release() had just reset.
|
|
39
|
+
std::recursive_mutex playerMutex;
|
|
40
|
+
void release();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#include "RNSVCheckedSizes.h"
|
|
2
|
+
#include "NativeEventDispatcher.h"
|
|
3
|
+
#include "NativeRgbaBuffer.h"
|
|
4
|
+
#include "NativeHardwareBuffer.h"
|
|
5
|
+
#include "RNSVMemoryBudget.h"
|
|
6
|
+
#include "VideoCapabilities.h"
|
|
7
|
+
#include "VideoCompositionFramesExtractorHostObject.h"
|
|
8
|
+
#include "VideoCompositionFramesExtractorSyncHostObject.h"
|
|
9
|
+
#include "VideoEncoderHostObject.h"
|
|
10
|
+
#include "VideoPlayerHostObject.h"
|
|
11
|
+
#include <fbjni/fbjni.h>
|
|
12
|
+
#include <jni.h>
|
|
13
|
+
#include <jsi/jsi.h>
|
|
14
|
+
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
using namespace RNSkiaVideo;
|
|
17
|
+
|
|
18
|
+
void install(jsi::Runtime& jsiRuntime) {
|
|
19
|
+
|
|
20
|
+
auto RNSVModule = jsi::Object(jsiRuntime);
|
|
21
|
+
MemoryBudget::install(jsiRuntime, RNSVModule);
|
|
22
|
+
RNSVModule.setProperty(jsiRuntime, "configureNativeBufferInterop",
|
|
23
|
+
jsi::Function::createFromHostFunction(jsiRuntime,
|
|
24
|
+
jsi::PropNameID::forAscii(jsiRuntime, "configureNativeBufferInterop"), 1,
|
|
25
|
+
[](jsi::Runtime& runtime, const jsi::Value&, const jsi::Value* args, size_t count) -> jsi::Value {
|
|
26
|
+
if (count != 1 || !args[0].isBool())
|
|
27
|
+
throw jsi::JSError(runtime, "configureNativeBufferInterop requires a boolean");
|
|
28
|
+
try { NativeHardwareBuffer::configure(args[0].getBool()); }
|
|
29
|
+
catch (const std::exception& error) { throw jsi::JSError(runtime, error.what()); }
|
|
30
|
+
return jsi::Value::undefined();
|
|
31
|
+
}));
|
|
32
|
+
RNSVModule.setProperty(jsiRuntime, "getBackendInfo",
|
|
33
|
+
jsi::Function::createFromHostFunction(jsiRuntime,
|
|
34
|
+
jsi::PropNameID::forAscii(jsiRuntime, "getBackendInfo"), 0,
|
|
35
|
+
[](jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value {
|
|
36
|
+
auto info = jsi::Object(runtime);
|
|
37
|
+
info.setProperty(runtime, "platform", jsi::String::createFromAscii(runtime, "android"));
|
|
38
|
+
const bool hardware = NativeHardwareBuffer::isEnabled();
|
|
39
|
+
info.setProperty(runtime, "decodeTransport", jsi::String::createFromAscii(runtime,
|
|
40
|
+
hardware ? "ahardwarebuffer-rgba-egl" : "cpu-rgba-readback"));
|
|
41
|
+
// Export renders into encoder-lent AHardwareBuffers, sampled by the
|
|
42
|
+
// encoder's EGL context; CPU readback stays the fallback.
|
|
43
|
+
info.setProperty(runtime, "encodeTransport", jsi::String::createFromAscii(runtime,
|
|
44
|
+
hardware ? "ahardwarebuffer-egl-sample" : "cpu-rgba-upload"));
|
|
45
|
+
info.setProperty(runtime, "exportFallback", jsi::String::createFromAscii(runtime, "cpu-rgba-upload"));
|
|
46
|
+
info.setProperty(runtime, "gpuDirectExport", hardware);
|
|
47
|
+
info.setProperty(runtime, "decodeCpuCopiesBeforeSkia", hardware ? 0 : 1);
|
|
48
|
+
info.setProperty(runtime, "cpuReadbackDecode", !hardware);
|
|
49
|
+
info.setProperty(runtime, "ownedFrameTransport", jsi::String::createFromAscii(runtime,
|
|
50
|
+
hardware ? "immutable-ahardwarebuffer-shared-leases" : "direct-buffer-java-scoped-copy"));
|
|
51
|
+
info.setProperty(runtime, "skiaImportTransport", jsi::String::createFromAscii(runtime,
|
|
52
|
+
hardware ? "native-buffer-webgpu-blit-snapshot" : "cpu-rgba-scoped-copy-webgpu-upload-snapshot"));
|
|
53
|
+
info.setProperty(runtime, "zeroCopyDecode", false);
|
|
54
|
+
info.setProperty(runtime, "zeroCopyEncode", false);
|
|
55
|
+
return info;
|
|
56
|
+
}));
|
|
57
|
+
auto createVideoPlayer = jsi::Function::createFromHostFunction(
|
|
58
|
+
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "createVideoPlayer"), 2,
|
|
59
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
60
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
61
|
+
if (count < 1 || !arguments[0].isString()) {
|
|
62
|
+
throw jsi::JSError(runtime,
|
|
63
|
+
"ReactNativeSkiaVideo.createVideoPlayer(..) "
|
|
64
|
+
"expects two arguments (string, object)!");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
int width = -1;
|
|
68
|
+
int height = -1;
|
|
69
|
+
if (count >= 2 && arguments[1].isObject()) {
|
|
70
|
+
auto res = arguments[1].asObject(runtime);
|
|
71
|
+
width = checkedPositiveInt(res.getProperty(runtime, "width").asNumber(), "decode width");
|
|
72
|
+
height = checkedPositiveInt(res.getProperty(runtime, "height").asNumber(), "decode height");
|
|
73
|
+
}
|
|
74
|
+
auto instance = std::make_shared<VideoPlayerHostObject>(
|
|
75
|
+
runtime, arguments[0].asString(runtime).utf8(runtime), width,
|
|
76
|
+
height);
|
|
77
|
+
instance->initialize(arguments[0].asString(runtime).utf8(runtime), width, height);
|
|
78
|
+
|
|
79
|
+
return jsi::Object::createFromHostObject(runtime, instance);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
RNSVModule.setProperty(jsiRuntime, "createVideoPlayer",
|
|
83
|
+
std::move(createVideoPlayer));
|
|
84
|
+
|
|
85
|
+
auto createVideoCompositionFramesExtractor =
|
|
86
|
+
jsi::Function::createFromHostFunction(
|
|
87
|
+
jsiRuntime,
|
|
88
|
+
jsi::PropNameID::forAscii(jsiRuntime,
|
|
89
|
+
"createVideoCompositionFramesExtractor"),
|
|
90
|
+
1,
|
|
91
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
92
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
93
|
+
if (count != 1 || !arguments[0].isObject()) {
|
|
94
|
+
throw jsi::JSError(runtime,
|
|
95
|
+
"SkiaVideo.createRNSVCompositionPlayer(.."
|
|
96
|
+
") expects one arguments (object)!");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
auto instance =
|
|
100
|
+
std::make_shared<VideoCompositionFramesExtractorHostObject>(
|
|
101
|
+
runtime, arguments[0].asObject(runtime));
|
|
102
|
+
instance->initialize(runtime, arguments[0].asObject(runtime));
|
|
103
|
+
|
|
104
|
+
return jsi::Object::createFromHostObject(runtime, instance);
|
|
105
|
+
});
|
|
106
|
+
RNSVModule.setProperty(jsiRuntime, "createVideoCompositionFramesExtractor",
|
|
107
|
+
std::move(createVideoCompositionFramesExtractor));
|
|
108
|
+
|
|
109
|
+
auto createVideoCompositionFramesExtractorSync =
|
|
110
|
+
jsi::Function::createFromHostFunction(
|
|
111
|
+
jsiRuntime,
|
|
112
|
+
jsi::PropNameID::forAscii(
|
|
113
|
+
jsiRuntime, "createVideoCompositionFramesExtractorSync"),
|
|
114
|
+
1,
|
|
115
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
116
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
117
|
+
if (count != 1 || !arguments[0].isObject()) {
|
|
118
|
+
throw jsi::JSError(runtime,
|
|
119
|
+
"createVideoCompositionFramesExtractorSync(.."
|
|
120
|
+
") expects one arguments (object)!");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
auto instance =
|
|
124
|
+
std::make_shared<VideoCompositionFramesExtractorSyncHostObject>(
|
|
125
|
+
runtime, arguments[0].asObject(runtime));
|
|
126
|
+
return jsi::Object::createFromHostObject(runtime, instance);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
RNSVModule.setProperty(jsiRuntime,
|
|
130
|
+
"createVideoCompositionFramesExtractorSync",
|
|
131
|
+
std::move(createVideoCompositionFramesExtractorSync));
|
|
132
|
+
|
|
133
|
+
auto createVideoEncoder = jsi::Function::createFromHostFunction(
|
|
134
|
+
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "createVideoEncoder"),
|
|
135
|
+
2,
|
|
136
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
137
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
138
|
+
if (count < 1 || !arguments[0].isObject()) {
|
|
139
|
+
throw jsi::JSError(runtime, "ReactNativeSkiaVideo."
|
|
140
|
+
"createVideoEncoder(.."
|
|
141
|
+
") expects one arguments (object)!");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
auto options = arguments[0].asObject(runtime);
|
|
145
|
+
auto outPath = options.getProperty(runtime, "outPath")
|
|
146
|
+
.asString(runtime)
|
|
147
|
+
.utf8(runtime);
|
|
148
|
+
int width = checkedPositiveInt(options.getProperty(runtime, "width").asNumber(), "encoder width");
|
|
149
|
+
int height = checkedPositiveInt(options.getProperty(runtime, "height").asNumber(), "encoder height");
|
|
150
|
+
int frameRate =
|
|
151
|
+
checkedPositiveInt(options.getProperty(runtime, "frameRate").asNumber(), "encoder frame rate");
|
|
152
|
+
int bitRate = checkedPositiveInt(options.getProperty(runtime, "bitRate").asNumber(), "encoder bitrate");
|
|
153
|
+
std::optional<std::string> encoderName = std::nullopt;
|
|
154
|
+
if (options.hasProperty(runtime, "encoderName")) {
|
|
155
|
+
auto value = options.getProperty(runtime, "encoderName");
|
|
156
|
+
if (value.isString()) {
|
|
157
|
+
encoderName = value.asString(runtime).utf8(runtime);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
std::optional<std::string> codec = std::nullopt;
|
|
161
|
+
if (options.hasProperty(runtime, "codec")) {
|
|
162
|
+
auto value = options.getProperty(runtime, "codec");
|
|
163
|
+
if (value.isString()) {
|
|
164
|
+
codec = value.asString(runtime).utf8(runtime);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
int audioSampleRate = 44100;
|
|
168
|
+
int audioChannelCount = 2;
|
|
169
|
+
int audioBitRate = 128000;
|
|
170
|
+
if (options.hasProperty(runtime, "audioSampleRate")) {
|
|
171
|
+
auto value = options.getProperty(runtime, "audioSampleRate");
|
|
172
|
+
if (value.isNumber()) {
|
|
173
|
+
audioSampleRate = checkedPositiveInt(value.asNumber(), "audio sample rate");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (options.hasProperty(runtime, "audioChannelCount")) {
|
|
177
|
+
auto value = options.getProperty(runtime, "audioChannelCount");
|
|
178
|
+
if (value.isNumber()) {
|
|
179
|
+
audioChannelCount = checkedPositiveInt(value.asNumber(), "audio channel count");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (options.hasProperty(runtime, "audioBitRate")) {
|
|
183
|
+
auto value = options.getProperty(runtime, "audioBitRate");
|
|
184
|
+
if (value.isNumber()) {
|
|
185
|
+
audioBitRate = checkedPositiveInt(value.asNumber(), "audio bitrate");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
local_ref<VideoComposition> composition = nullptr;
|
|
190
|
+
if (count >= 2 && arguments[1].isObject()) {
|
|
191
|
+
auto jsComposition = arguments[1].asObject(runtime);
|
|
192
|
+
composition = VideoComposition::fromJSIObject(runtime, jsComposition);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
auto instance = std::make_shared<VideoEncoderHostObject>(
|
|
196
|
+
outPath, width, height, frameRate, bitRate, encoderName, codec,
|
|
197
|
+
composition, audioSampleRate, audioChannelCount, audioBitRate);
|
|
198
|
+
return jsi::Object::createFromHostObject(runtime, instance);
|
|
199
|
+
});
|
|
200
|
+
RNSVModule.setProperty(jsiRuntime, "createVideoEncoder",
|
|
201
|
+
std::move(createVideoEncoder));
|
|
202
|
+
|
|
203
|
+
auto isEncodingSupported = jsi::Function::createFromHostFunction(
|
|
204
|
+
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "isEncodingSupported"),
|
|
205
|
+
1,
|
|
206
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
207
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
208
|
+
if (count < 1 || !arguments[0].isString()) {
|
|
209
|
+
throw jsi::JSError(runtime, "ReactNativeSkiaVideo."
|
|
210
|
+
"isEncodingSupported(..) expects a codec "
|
|
211
|
+
"name!");
|
|
212
|
+
}
|
|
213
|
+
auto codec = arguments[0].asString(runtime).utf8(runtime);
|
|
214
|
+
return jsi::Value(VideoEncoder::isCodecSupported(codec));
|
|
215
|
+
});
|
|
216
|
+
RNSVModule.setProperty(jsiRuntime, "isEncodingSupported",
|
|
217
|
+
std::move(isEncodingSupported));
|
|
218
|
+
|
|
219
|
+
auto getDecodingCapabilitiesFor = jsi::Function::createFromHostFunction(
|
|
220
|
+
jsiRuntime,
|
|
221
|
+
jsi::PropNameID::forAscii(jsiRuntime, "getDecodingCapabilitiesFor"), 1,
|
|
222
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
223
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
224
|
+
if (count < 1 || !arguments[0].isString())
|
|
225
|
+
throw jsi::JSError(runtime, "getDecodingCapabilitiesFor expects a MIME type");
|
|
226
|
+
auto mimetype = arguments[0].asString(runtime).utf8(runtime);
|
|
227
|
+
|
|
228
|
+
auto decoderInfo =
|
|
229
|
+
VideoCapabilities::getDecodingCapabilitiesFor(mimetype);
|
|
230
|
+
if (decoderInfo == nullptr) {
|
|
231
|
+
return jsi::Value::null();
|
|
232
|
+
}
|
|
233
|
+
auto result = jsi::Object(runtime);
|
|
234
|
+
result.setProperty(runtime, "maxInstances",
|
|
235
|
+
jsi::Value(decoderInfo->getMaxInstances()));
|
|
236
|
+
result.setProperty(runtime, "maxWidth",
|
|
237
|
+
jsi::Value(decoderInfo->getMaxWidth()));
|
|
238
|
+
result.setProperty(runtime, "maxHeight",
|
|
239
|
+
jsi::Value(decoderInfo->getMaxHeight()));
|
|
240
|
+
|
|
241
|
+
return result;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
RNSVModule.setProperty(jsiRuntime, "getDecodingCapabilitiesFor",
|
|
245
|
+
std::move(getDecodingCapabilitiesFor));
|
|
246
|
+
|
|
247
|
+
auto getValidEncoderConfigurations = jsi::Function::createFromHostFunction(
|
|
248
|
+
jsiRuntime,
|
|
249
|
+
jsi::PropNameID::forAscii(jsiRuntime, "getValidEncoderConfigurations"),
|
|
250
|
+
4,
|
|
251
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
252
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
253
|
+
if (count < 4) throw jsi::JSError(runtime, "getValidEncoderConfigurations expects dimensions, frame rate and bitrate");
|
|
254
|
+
int width = checkedPositiveInt(arguments[0].asNumber(), "encoder width");
|
|
255
|
+
int height = checkedPositiveInt(arguments[1].asNumber(), "encoder height");
|
|
256
|
+
int framerate = checkedPositiveInt(arguments[2].asNumber(), "encoder frame rate");
|
|
257
|
+
int bitrate = checkedPositiveInt(arguments[3].asNumber(), "encoder bitrate");
|
|
258
|
+
|
|
259
|
+
std::optional<std::string> codec = std::nullopt;
|
|
260
|
+
if (count > 4 && arguments[4].isString()) {
|
|
261
|
+
codec = arguments[4].asString(runtime).utf8(runtime);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
auto encoderInfos = VideoCapabilities::getValidEncoderConfigurations(
|
|
265
|
+
width, height, framerate, bitrate, codec);
|
|
266
|
+
|
|
267
|
+
if (encoderInfos == nullptr) {
|
|
268
|
+
return jsi::Value::null();
|
|
269
|
+
}
|
|
270
|
+
auto result = jsi::Array(runtime, encoderInfos->size());
|
|
271
|
+
size_t i = 0;
|
|
272
|
+
for (const auto& encoderInfo : *encoderInfos) {
|
|
273
|
+
auto jsObject = jsi::Object(runtime);
|
|
274
|
+
jsObject.setProperty(runtime, "encoderName",
|
|
275
|
+
jsi::String::createFromUtf8(
|
|
276
|
+
runtime, encoderInfo->getEncoderName()));
|
|
277
|
+
jsObject.setProperty(
|
|
278
|
+
runtime, "hardwareAccelerated",
|
|
279
|
+
jsi::Value(encoderInfo->getHardwareAccelerated()));
|
|
280
|
+
jsObject.setProperty(runtime, "width",
|
|
281
|
+
jsi::Value(encoderInfo->getWidth()));
|
|
282
|
+
jsObject.setProperty(runtime, "height",
|
|
283
|
+
jsi::Value(encoderInfo->getHeight()));
|
|
284
|
+
jsObject.setProperty(runtime, "frameRate",
|
|
285
|
+
jsi::Value(encoderInfo->getFrameRate()));
|
|
286
|
+
jsObject.setProperty(runtime, "bitRate",
|
|
287
|
+
jsi::Value(encoderInfo->getBitrate()));
|
|
288
|
+
|
|
289
|
+
result.setValueAtIndex(runtime, i, jsObject);
|
|
290
|
+
i++;
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
RNSVModule.setProperty(jsiRuntime, "getValidEncoderConfigurations",
|
|
296
|
+
std::move(getValidEncoderConfigurations));
|
|
297
|
+
|
|
298
|
+
jsiRuntime.global().setProperty(jsiRuntime, "RNSkiaVideo",
|
|
299
|
+
std::move(RNSVModule));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
extern "C" JNIEXPORT void JNICALL
|
|
303
|
+
Java_com_azzapp_rnskv_ReactNativeSkiaVideoModule_nativeInstall(JNIEnv* env,
|
|
304
|
+
jobject clazz,
|
|
305
|
+
jlong jsiPtr) {
|
|
306
|
+
auto runtime = reinterpret_cast<jsi::Runtime*>(jsiPtr);
|
|
307
|
+
if (runtime) {
|
|
308
|
+
install(*runtime);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
jint JNI_OnLoad(JavaVM* vm, void*) {
|
|
313
|
+
return facebook::jni::initialize(
|
|
314
|
+
vm, [] { NativeEventDispatcher::registerNatives(); NativeRgbaBuffer::registerNatives();
|
|
315
|
+
NativeHardwareBuffer::registerNatives(); });
|
|
316
|
+
}
|