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
package/ios/VideoFrame.h
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Based on the MIT-licensed Skia Video implementation by François de Campredon.
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#import <CoreVideo/CoreVideo.h>
|
|
5
|
+
#import <jsi/jsi.h>
|
|
6
|
+
#include <list>
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <mutex>
|
|
9
|
+
|
|
10
|
+
namespace RNSkiaVideo {
|
|
11
|
+
using namespace facebook;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Scoped immutable decoder buffer. The pointer is a CVPixelBufferRef, never
|
|
15
|
+
* a Metal texture or IOSurfaceRef. Complete sampling (or make an owned image
|
|
16
|
+
* and wait for that copy) before dispose(). No age-based buffer retirement.
|
|
17
|
+
*/
|
|
18
|
+
class JSI_EXPORT VideoFrame : public jsi::HostObject,
|
|
19
|
+
public std::enable_shared_from_this<VideoFrame> {
|
|
20
|
+
public:
|
|
21
|
+
VideoFrame(CVPixelBufferRef buffer, double width, double height, int rotation,
|
|
22
|
+
uint64_t producerId, uint64_t transferredMemoryToken = 0);
|
|
23
|
+
~VideoFrame() override;
|
|
24
|
+
void releaseBuffer();
|
|
25
|
+
bool hasBuffer() const;
|
|
26
|
+
size_t bufferBytes() const;
|
|
27
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
|
|
28
|
+
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
|
|
29
|
+
|
|
30
|
+
private:
|
|
31
|
+
#if defined(RNSV_NATIVE_TESTS)
|
|
32
|
+
friend class NativeFrameTestDriver;
|
|
33
|
+
#endif
|
|
34
|
+
mutable std::mutex mutex;
|
|
35
|
+
CVPixelBufferRef pixelBuffer = NULL;
|
|
36
|
+
size_t bytes = 0;
|
|
37
|
+
uint64_t memoryToken = 0;
|
|
38
|
+
uint64_t frameId;
|
|
39
|
+
uint64_t producerId;
|
|
40
|
+
double width;
|
|
41
|
+
double height;
|
|
42
|
+
int rotation;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compatibility name for the bounded outstanding-frame registry. Weak entries
|
|
47
|
+
* free their slot after disposal/collection. Full means backpressure; a live
|
|
48
|
+
* consumer buffer is never destroyed to admit another frame.
|
|
49
|
+
*/
|
|
50
|
+
class VideoFrameRing {
|
|
51
|
+
public:
|
|
52
|
+
explicit VideoFrameRing(size_t capacity = 2) : capacity(capacity) {}
|
|
53
|
+
bool canAcquire();
|
|
54
|
+
void push(const std::shared_ptr<VideoFrame>& frame);
|
|
55
|
+
// Stop tracking; outstanding consumers retain their own immutable buffer.
|
|
56
|
+
void releaseAll();
|
|
57
|
+
|
|
58
|
+
private:
|
|
59
|
+
std::mutex mutex;
|
|
60
|
+
size_t capacity;
|
|
61
|
+
std::list<std::weak_ptr<VideoFrame>> frames;
|
|
62
|
+
void removeReleased();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
uint64_t allocateVideoProducerId();
|
|
66
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Based on the MIT-licensed Skia Video implementation by François de Campredon.
|
|
2
|
+
#import "VideoFrame.h"
|
|
3
|
+
#import "NativeResourceStats.h"
|
|
4
|
+
#include "RNSVMemoryBudget.h"
|
|
5
|
+
#include <atomic>
|
|
6
|
+
#include <stdexcept>
|
|
7
|
+
|
|
8
|
+
namespace RNSkiaVideo {
|
|
9
|
+
static std::atomic<uint64_t> nextFrameId{1};
|
|
10
|
+
static std::atomic<uint64_t> nextProducerId{1};
|
|
11
|
+
uint64_t allocateVideoProducerId() { return nextProducerId.fetch_add(1); }
|
|
12
|
+
|
|
13
|
+
VideoFrame::VideoFrame(CVPixelBufferRef buffer, double, double, int rotation,
|
|
14
|
+
uint64_t producerId, uint64_t transferredMemoryToken)
|
|
15
|
+
: frameId(nextFrameId.fetch_add(1)), producerId(producerId),
|
|
16
|
+
width(CVPixelBufferGetWidth(buffer)), height(CVPixelBufferGetHeight(buffer)),
|
|
17
|
+
rotation(rotation) {
|
|
18
|
+
bytes = pixelBufferBytes(buffer);
|
|
19
|
+
memoryToken = transferredMemoryToken ? transferredMemoryToken :
|
|
20
|
+
MemoryBudget::instance().reserve(bytes, "iOS decoded frame");
|
|
21
|
+
pixelBuffer = CVPixelBufferRetain(buffer);
|
|
22
|
+
NativeResourceStats::frameBuffers.add(1);
|
|
23
|
+
NativeResourceStats::frameBytes.add(bytes);
|
|
24
|
+
}
|
|
25
|
+
VideoFrame::~VideoFrame() { releaseBuffer(); }
|
|
26
|
+
void VideoFrame::releaseBuffer() {
|
|
27
|
+
std::lock_guard<std::mutex> guard(mutex);
|
|
28
|
+
if (pixelBuffer) {
|
|
29
|
+
CVPixelBufferRelease(pixelBuffer);
|
|
30
|
+
pixelBuffer = NULL;
|
|
31
|
+
NativeResourceStats::frameBuffers.remove(1);
|
|
32
|
+
NativeResourceStats::frameBytes.remove(bytes);
|
|
33
|
+
MemoryBudget::instance().release(memoryToken);
|
|
34
|
+
memoryToken = 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
bool VideoFrame::hasBuffer() const {
|
|
38
|
+
std::lock_guard<std::mutex> guard(mutex);
|
|
39
|
+
return pixelBuffer != NULL;
|
|
40
|
+
}
|
|
41
|
+
size_t VideoFrame::bufferBytes() const { return bytes; }
|
|
42
|
+
void VideoFrameRing::removeReleased() {
|
|
43
|
+
for (auto it = frames.begin(); it != frames.end();) {
|
|
44
|
+
auto frame = it->lock();
|
|
45
|
+
if (!frame || !frame->hasBuffer()) it = frames.erase(it);
|
|
46
|
+
else ++it;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
bool VideoFrameRing::canAcquire() {
|
|
50
|
+
std::lock_guard<std::mutex> guard(mutex);
|
|
51
|
+
removeReleased();
|
|
52
|
+
return frames.size() < capacity;
|
|
53
|
+
}
|
|
54
|
+
void VideoFrameRing::push(const std::shared_ptr<VideoFrame>& frame) {
|
|
55
|
+
std::lock_guard<std::mutex> guard(mutex);
|
|
56
|
+
removeReleased();
|
|
57
|
+
if (frames.size() >= capacity) {
|
|
58
|
+
throw std::runtime_error("Native video frame capacity reached; dispose "
|
|
59
|
+
"consumed frames after completing GPU work");
|
|
60
|
+
}
|
|
61
|
+
frames.push_back(frame);
|
|
62
|
+
}
|
|
63
|
+
void VideoFrameRing::releaseAll() {
|
|
64
|
+
std::lock_guard<std::mutex> guard(mutex);
|
|
65
|
+
// Removing the producer cannot revoke a consumer already sampling this
|
|
66
|
+
// buffer. Its explicit dispose()/final shared owner releases the lease.
|
|
67
|
+
frames.clear();
|
|
68
|
+
}
|
|
69
|
+
std::vector<jsi::PropNameID> VideoFrame::getPropertyNames(jsi::Runtime& rt) {
|
|
70
|
+
std::vector<jsi::PropNameID> result;
|
|
71
|
+
for (const auto* name : {"id", "producerId", "width", "height", "rotation",
|
|
72
|
+
"texture", "nativeBuffer", "dispose"}) {
|
|
73
|
+
result.push_back(jsi::PropNameID::forAscii(rt, name));
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
jsi::Value VideoFrame::get(jsi::Runtime& runtime,
|
|
78
|
+
const jsi::PropNameID& propNameId) {
|
|
79
|
+
auto name = propNameId.utf8(runtime);
|
|
80
|
+
if (name == "id") return jsi::Value(static_cast<double>(frameId));
|
|
81
|
+
if (name == "producerId") return jsi::Value(static_cast<double>(producerId));
|
|
82
|
+
if (name == "width") return jsi::Value(width);
|
|
83
|
+
if (name == "height") return jsi::Value(height);
|
|
84
|
+
if (name == "rotation") return jsi::Value(rotation);
|
|
85
|
+
if (name == "dispose") {
|
|
86
|
+
std::weak_ptr<VideoFrame> weak = shared_from_this();
|
|
87
|
+
return jsi::Function::createFromHostFunction(
|
|
88
|
+
runtime, jsi::PropNameID::forAscii(runtime, "dispose"), 0,
|
|
89
|
+
[weak](jsi::Runtime&, const jsi::Value&, const jsi::Value*, size_t) {
|
|
90
|
+
if (auto frame = weak.lock()) frame->releaseBuffer();
|
|
91
|
+
return jsi::Value::undefined();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (name == "texture" || name == "nativeBuffer") {
|
|
95
|
+
std::lock_guard<std::mutex> guard(mutex);
|
|
96
|
+
if (!pixelBuffer) return jsi::Value::undefined();
|
|
97
|
+
auto pointer = jsi::BigInt::fromUint64(
|
|
98
|
+
runtime, reinterpret_cast<uintptr_t>(pixelBuffer));
|
|
99
|
+
if (name == "nativeBuffer") return pointer;
|
|
100
|
+
auto descriptor = jsi::Object(runtime);
|
|
101
|
+
descriptor.setProperty(runtime, "kind",
|
|
102
|
+
jsi::String::createFromAscii(runtime, "native-buffer"));
|
|
103
|
+
descriptor.setProperty(runtime, "nativeBuffer", pointer);
|
|
104
|
+
return descriptor;
|
|
105
|
+
}
|
|
106
|
+
return jsi::Value::undefined();
|
|
107
|
+
}
|
|
108
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "RNSVEventEmitter.h"
|
|
4
|
+
#include "RNSVHostObject.h"
|
|
5
|
+
#include "RNSVVideoPlayer.h"
|
|
6
|
+
#include "VideoFrame.h"
|
|
7
|
+
|
|
8
|
+
using namespace facebook;
|
|
9
|
+
namespace RNSkiaVideo { class VideoPlayerHostObject; }
|
|
10
|
+
|
|
11
|
+
@interface RNSVSkiaVideoPlayerDelegateImpl : NSObject <RNSVVideoPlayerDelegate>
|
|
12
|
+
|
|
13
|
+
- (instancetype)initWithHost:
|
|
14
|
+
(std::weak_ptr<RNSkiaVideo::VideoPlayerHostObject>)host;
|
|
15
|
+
- (void)dispose;
|
|
16
|
+
|
|
17
|
+
@end
|
|
18
|
+
|
|
19
|
+
namespace RNSkiaVideo {
|
|
20
|
+
|
|
21
|
+
class JSI_EXPORT VideoPlayerHostObject : public RNSVHostObject, public EventEmitter {
|
|
22
|
+
public:
|
|
23
|
+
VideoPlayerHostObject(jsi::Runtime& runtime,
|
|
24
|
+
std::shared_ptr<react::CallInvoker> callInvoker,
|
|
25
|
+
NSURL* url, CGSize resolution, bool directTexture);
|
|
26
|
+
~VideoPlayerHostObject();
|
|
27
|
+
void initialize(NSURL* url, CGSize resolution);
|
|
28
|
+
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
|
|
29
|
+
void set(jsi::Runtime&, const jsi::PropNameID& name,
|
|
30
|
+
const jsi::Value& value) override;
|
|
31
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
|
|
32
|
+
|
|
33
|
+
void readyToPlay(float width, float height, int rotation);
|
|
34
|
+
void frameAvailableEventHandler(CMTime time);
|
|
35
|
+
|
|
36
|
+
private:
|
|
37
|
+
NSObject* lock;
|
|
38
|
+
RNSVVideoPlayer* player;
|
|
39
|
+
RNSVSkiaVideoPlayerDelegateImpl* playerDelegate;
|
|
40
|
+
std::shared_ptr<VideoFrame> currentFrame;
|
|
41
|
+
VideoFrameRing frameRing;
|
|
42
|
+
uint64_t producerId = allocateVideoProducerId();
|
|
43
|
+
std::shared_ptr<VideoFrame> makeFrame(CVPixelBufferRef buffer);
|
|
44
|
+
CMTime lastFrameAvailable = kCMTimeInvalid;
|
|
45
|
+
CMTime lastFrameDrawn = kCMTimeInvalid;
|
|
46
|
+
float width = 0;
|
|
47
|
+
float height = 0;
|
|
48
|
+
int rotation = 0;
|
|
49
|
+
std::atomic_flag released = ATOMIC_FLAG_INIT;
|
|
50
|
+
void release();
|
|
51
|
+
};
|
|
52
|
+
} // namespace RNSkiaVideo
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
//
|
|
2
|
+
// VideoPlayerHostObject.m
|
|
3
|
+
// azzapp-react-native-skia-video
|
|
4
|
+
//
|
|
5
|
+
// Created by François de Campredon on 03/05/2024.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "VideoPlayerHostObject.h"
|
|
9
|
+
#import "RNSVJSIUtils.h"
|
|
10
|
+
#import "RNSVScopedPixelBuffer.h"
|
|
11
|
+
#include "RNSVCheckedSizes.h"
|
|
12
|
+
|
|
13
|
+
namespace RNSkiaVideo {
|
|
14
|
+
using namespace facebook;
|
|
15
|
+
|
|
16
|
+
VideoPlayerHostObject::VideoPlayerHostObject(
|
|
17
|
+
jsi::Runtime& runtime, std::shared_ptr<react::CallInvoker> callInvoker,
|
|
18
|
+
NSURL* url, CGSize resolution, bool directTexture)
|
|
19
|
+
: EventEmitter(runtime, callInvoker) {
|
|
20
|
+
lock = [[NSObject alloc] init];
|
|
21
|
+
(void)directTexture;
|
|
22
|
+
(void)url;
|
|
23
|
+
(void)resolution;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void VideoPlayerHostObject::initialize(NSURL* url, CGSize resolution) {
|
|
27
|
+
playerDelegate = [[RNSVSkiaVideoPlayerDelegateImpl alloc]
|
|
28
|
+
initWithHost:std::weak_ptr<VideoPlayerHostObject>(
|
|
29
|
+
std::static_pointer_cast<VideoPlayerHostObject>(shared_from_this()))];
|
|
30
|
+
player = [[RNSVVideoPlayer alloc] initWithURL:url
|
|
31
|
+
delegate:playerDelegate
|
|
32
|
+
resolution:resolution];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
VideoPlayerHostObject::~VideoPlayerHostObject() {
|
|
36
|
+
release();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
std::vector<jsi::PropNameID>
|
|
40
|
+
VideoPlayerHostObject::getPropertyNames(jsi::Runtime& rt) {
|
|
41
|
+
std::vector<jsi::PropNameID> result;
|
|
42
|
+
result.push_back(
|
|
43
|
+
jsi::PropNameID::forUtf8(rt, std::string("decodeNextFrame")));
|
|
44
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("play")));
|
|
45
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("pause")));
|
|
46
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("seekTo")));
|
|
47
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("currentTime")));
|
|
48
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("duration")));
|
|
49
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("volume")));
|
|
50
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("playbackSpeed")));
|
|
51
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isLooping")));
|
|
52
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isPlaying")));
|
|
53
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("dispose")));
|
|
54
|
+
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("on")));
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The methods are created once per runtime (see RNSVHostObject):
|
|
59
|
+
// `decodeNextFrame` is read by useVideoPlayer at every vsync of the UI
|
|
60
|
+
// runtime, and a fresh host function on each read is two garbage collected
|
|
61
|
+
// allocations per frame for nothing.
|
|
62
|
+
jsi::Value VideoPlayerHostObject::get(jsi::Runtime& runtime,
|
|
63
|
+
const jsi::PropNameID& propNameId) {
|
|
64
|
+
@synchronized(lock) {
|
|
65
|
+
auto propName = propNameId.utf8(runtime);
|
|
66
|
+
if (propName == "decodeNextFrame") {
|
|
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
|
+
@synchronized(lock) {
|
|
72
|
+
if (released.test() || !CMTIME_IS_VALID(lastFrameAvailable) ||
|
|
73
|
+
CMTimeCompare(lastFrameDrawn, lastFrameAvailable) == 0) {
|
|
74
|
+
return jsi::Value::null();
|
|
75
|
+
}
|
|
76
|
+
if (!frameRing.canAcquire()) return jsi::Value::null();
|
|
77
|
+
ScopedPixelBuffer buffer([player copyPixelBufferForTime:lastFrameAvailable]);
|
|
78
|
+
if (!buffer.get()) {
|
|
79
|
+
return jsi::Value::null();
|
|
80
|
+
}
|
|
81
|
+
currentFrame = makeFrame(buffer.get());
|
|
82
|
+
lastFrameDrawn = lastFrameAvailable;
|
|
83
|
+
return jsi::Object::createFromHostObject(runtime, currentFrame);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
} else if (propName == "play") {
|
|
87
|
+
return getFunction(
|
|
88
|
+
runtime, propName, 0,
|
|
89
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
90
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
91
|
+
@synchronized(lock) { if (!released.test()) {
|
|
92
|
+
[player play];
|
|
93
|
+
} }
|
|
94
|
+
return jsi::Value::undefined();
|
|
95
|
+
});
|
|
96
|
+
} else if (propName == "pause") {
|
|
97
|
+
return getFunction(
|
|
98
|
+
runtime, propName, 0,
|
|
99
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
100
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
101
|
+
@synchronized(lock) { if (!released.test()) {
|
|
102
|
+
[player pause];
|
|
103
|
+
} }
|
|
104
|
+
return jsi::Value::undefined();
|
|
105
|
+
});
|
|
106
|
+
} else if (propName == "seekTo") {
|
|
107
|
+
return getFunction(
|
|
108
|
+
runtime, propName, 1,
|
|
109
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
110
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
111
|
+
if (count < 1 || !arguments[0].isNumber())
|
|
112
|
+
throw jsi::JSError(runtime, "seekTo expects media seconds");
|
|
113
|
+
auto time = checkedMediaSeconds(arguments[0].asNumber());
|
|
114
|
+
@synchronized(lock) { if (!released.test()) {
|
|
115
|
+
std::weak_ptr<VideoPlayerHostObject> weak =
|
|
116
|
+
std::static_pointer_cast<VideoPlayerHostObject>(shared_from_this());
|
|
117
|
+
[player seekTo:CMTimeMakeWithSeconds(time, 600)
|
|
118
|
+
completionHandler:^(BOOL) {
|
|
119
|
+
if (auto owner = weak.lock()) {
|
|
120
|
+
if (!owner->released.test()) {
|
|
121
|
+
owner->emit("seekComplete", jsi::Value::null());
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}];
|
|
125
|
+
} }
|
|
126
|
+
return jsi::Value::undefined();
|
|
127
|
+
});
|
|
128
|
+
} else if (propName == "on") {
|
|
129
|
+
return getFunction(
|
|
130
|
+
runtime, propName, 2,
|
|
131
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
132
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
133
|
+
if (released.test()) {
|
|
134
|
+
// Nothing to listen to anymore: hand out a no-op unsubscribe.
|
|
135
|
+
return jsi::Function::createFromHostFunction(
|
|
136
|
+
runtime, jsi::PropNameID::forAscii(runtime, "dispose"), 0,
|
|
137
|
+
[](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
138
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
139
|
+
return jsi::Value::undefined();
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (count < 2 || !arguments[0].isString() || !arguments[1].isObject() ||
|
|
143
|
+
!arguments[1].asObject(runtime).isFunction(runtime))
|
|
144
|
+
throw jsi::JSError(runtime, "on expects an event name and listener");
|
|
145
|
+
auto name = arguments[0].asString(runtime).utf8(runtime);
|
|
146
|
+
auto handler = arguments[1].asObject(runtime).asFunction(runtime);
|
|
147
|
+
return this->on(name, std::move(handler));
|
|
148
|
+
});
|
|
149
|
+
} else if (propName == "dispose") {
|
|
150
|
+
return getFunction(
|
|
151
|
+
runtime, propName, 0,
|
|
152
|
+
[this](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
153
|
+
const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
154
|
+
this->release();
|
|
155
|
+
return jsi::Value::undefined();
|
|
156
|
+
});
|
|
157
|
+
} else if (propName == "currentTime") {
|
|
158
|
+
if (released.test()) {
|
|
159
|
+
return jsi::Value(0);
|
|
160
|
+
}
|
|
161
|
+
double seconds = CMTimeGetSeconds(player.currentTime);
|
|
162
|
+
return jsi::Value(isnan(seconds) ? -1 : seconds);
|
|
163
|
+
} else if (propName == "duration") {
|
|
164
|
+
if (released.test()) {
|
|
165
|
+
return jsi::Value(0);
|
|
166
|
+
}
|
|
167
|
+
double seconds = CMTimeGetSeconds(player.duration);
|
|
168
|
+
return jsi::Value(isnan(seconds) ? -1 : seconds);
|
|
169
|
+
} else if (propName == "volume") {
|
|
170
|
+
if (released.test()) {
|
|
171
|
+
return jsi::Value(0);
|
|
172
|
+
}
|
|
173
|
+
float volume = player.volume;
|
|
174
|
+
return jsi::Value(volume);
|
|
175
|
+
} else if (propName == "playbackSpeed") {
|
|
176
|
+
if (released.test()) {
|
|
177
|
+
return jsi::Value(1);
|
|
178
|
+
}
|
|
179
|
+
float playbackSpeed = player.playbackSpeed;
|
|
180
|
+
return jsi::Value(playbackSpeed);
|
|
181
|
+
} else if (propName == "isLooping") {
|
|
182
|
+
if (released.test()) {
|
|
183
|
+
return jsi::Value(false);
|
|
184
|
+
}
|
|
185
|
+
return jsi::Value(player.isLooping);
|
|
186
|
+
} else if (propName == "isPlaying") {
|
|
187
|
+
if (released.test()) {
|
|
188
|
+
return jsi::Value(false);
|
|
189
|
+
}
|
|
190
|
+
return jsi::Value(player.isPlaying);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return jsi::Value::undefined();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void VideoPlayerHostObject::set(jsi::Runtime& runtime,
|
|
198
|
+
const jsi::PropNameID& propNameId,
|
|
199
|
+
const jsi::Value& value) {
|
|
200
|
+
@synchronized(lock) {
|
|
201
|
+
if (released.test()) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
auto propName = propNameId.utf8(runtime);
|
|
205
|
+
if (propName == "volume") {
|
|
206
|
+
player.volume = value.asNumber();
|
|
207
|
+
} else if (propName == "playbackSpeed") {
|
|
208
|
+
player.playbackSpeed = value.asNumber();
|
|
209
|
+
} else if (propName == "isLooping") {
|
|
210
|
+
player.isLooping = value.asBool();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void VideoPlayerHostObject::frameAvailableEventHandler(CMTime time) {
|
|
216
|
+
if (released.test()) return;
|
|
217
|
+
@synchronized(lock) { if (!released.test()) lastFrameAvailable = time; }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void VideoPlayerHostObject::readyToPlay(float width, float height,
|
|
221
|
+
int rotation) {
|
|
222
|
+
if (released.test()) return;
|
|
223
|
+
@synchronized(lock) {
|
|
224
|
+
if (released.test()) return;
|
|
225
|
+
this->width = width;
|
|
226
|
+
this->height = height;
|
|
227
|
+
this->rotation = rotation;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
std::shared_ptr<VideoFrame>
|
|
232
|
+
VideoPlayerHostObject::makeFrame(CVPixelBufferRef buffer) {
|
|
233
|
+
auto frame = std::make_shared<VideoFrame>(buffer, width, height, rotation,
|
|
234
|
+
producerId);
|
|
235
|
+
frameRing.push(frame);
|
|
236
|
+
return frame;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
void VideoPlayerHostObject::release() {
|
|
240
|
+
if (!released.test_and_set()) {
|
|
241
|
+
removeAllListeners();
|
|
242
|
+
RNSVVideoPlayer* disposingPlayer;
|
|
243
|
+
RNSVSkiaVideoPlayerDelegateImpl* disposingDelegate;
|
|
244
|
+
@synchronized(lock) {
|
|
245
|
+
frameRing.releaseAll();
|
|
246
|
+
if (currentFrame) {
|
|
247
|
+
currentFrame = nullptr;
|
|
248
|
+
}
|
|
249
|
+
disposingDelegate = playerDelegate;
|
|
250
|
+
disposingPlayer = player;
|
|
251
|
+
playerDelegate = nil;
|
|
252
|
+
player = nil;
|
|
253
|
+
}
|
|
254
|
+
[disposingDelegate dispose];
|
|
255
|
+
[disposingPlayer dispose];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
} // namespace RNSkiaVideo
|
|
260
|
+
|
|
261
|
+
using namespace facebook;
|
|
262
|
+
|
|
263
|
+
@implementation RNSVSkiaVideoPlayerDelegateImpl {
|
|
264
|
+
std::weak_ptr<RNSkiaVideo::VideoPlayerHostObject> _host;
|
|
265
|
+
}
|
|
266
|
+
- (instancetype)initWithHost:
|
|
267
|
+
(std::weak_ptr<RNSkiaVideo::VideoPlayerHostObject>)host {
|
|
268
|
+
self = [super init];
|
|
269
|
+
if (self) _host = std::move(host);
|
|
270
|
+
return self;
|
|
271
|
+
}
|
|
272
|
+
- (void)readyToPlay:(NSDictionary*)assetInfos {
|
|
273
|
+
auto host = _host.lock();
|
|
274
|
+
if (!host) return;
|
|
275
|
+
float width = [(NSNumber*)assetInfos[@"width"] floatValue];
|
|
276
|
+
float height = [(NSNumber*)assetInfos[@"height"] floatValue];
|
|
277
|
+
int rotation = [(NSNumber*)assetInfos[@"rotation"] intValue];
|
|
278
|
+
host->readyToPlay(width, height, rotation);
|
|
279
|
+
host->emit("ready", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
280
|
+
auto result = jsi::Object(runtime);
|
|
281
|
+
result.setProperty(runtime, "width", width);
|
|
282
|
+
result.setProperty(runtime, "height", height);
|
|
283
|
+
result.setProperty(runtime, "rotation", rotation);
|
|
284
|
+
return result;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
- (void)frameAvailable:(CMTime)time {
|
|
288
|
+
if (auto host = _host.lock()) host->frameAvailableEventHandler(time);
|
|
289
|
+
}
|
|
290
|
+
- (void)bufferingStart {
|
|
291
|
+
if (auto host = _host.lock()) host->emit("bufferingStart");
|
|
292
|
+
}
|
|
293
|
+
- (void)bufferingEnd {
|
|
294
|
+
if (auto host = _host.lock()) host->emit("bufferingEnd");
|
|
295
|
+
}
|
|
296
|
+
- (void)bufferingUpdate:(NSArray<NSValue*>*)loadedTimeRanges {
|
|
297
|
+
if (auto host = _host.lock()) {
|
|
298
|
+
host->emit("bufferingUpdate", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
299
|
+
auto ranges = jsi::Array(runtime, loadedTimeRanges.count);
|
|
300
|
+
for (size_t i = 0; i < loadedTimeRanges.count; ++i) {
|
|
301
|
+
auto range = loadedTimeRanges[i].CMTimeRangeValue;
|
|
302
|
+
auto result = jsi::Object(runtime);
|
|
303
|
+
result.setProperty(runtime, "start", CMTimeGetSeconds(range.start));
|
|
304
|
+
result.setProperty(runtime, "duration", CMTimeGetSeconds(range.duration));
|
|
305
|
+
ranges.setValueAtIndex(runtime, i, result);
|
|
306
|
+
}
|
|
307
|
+
return ranges;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
- (void)videoError:(nullable NSError*)error {
|
|
312
|
+
if (auto host = _host.lock()) {
|
|
313
|
+
host->emit("error", [=](jsi::Runtime& runtime) -> jsi::Value {
|
|
314
|
+
return RNSkiaVideo::NSErrorToJSI(runtime, error);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
- (void)complete {
|
|
319
|
+
if (auto host = _host.lock()) host->emit("complete");
|
|
320
|
+
}
|
|
321
|
+
- (void)isPlaying:(BOOL)playing {
|
|
322
|
+
if (auto host = _host.lock()) {
|
|
323
|
+
host->emit("playingStatusChange", [playing](jsi::Runtime&) {
|
|
324
|
+
return jsi::Value(playing ? true : false);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
- (void)dispose {
|
|
329
|
+
// The weak reference deliberately remains unchanged: callbacks racing with
|
|
330
|
+
// host destruction either hold a valid owner or fail to lock it.
|
|
331
|
+
}
|
|
332
|
+
@end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativeReactNativeSkiaVideo.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AAMlD,eAAeA,mBAAmB,CAACC,GAAG,CAAO,sBAAsB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Platform } from "react-native";
|
|
4
|
+
import NativeReactNativeSkiaVideo from "./NativeReactNativeSkiaVideo.js";
|
|
5
|
+
if (!NativeReactNativeSkiaVideo) {
|
|
6
|
+
throw new Error(`The package 'react-native-skia-webgpu-video' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
|
7
|
+
ios: "- You have run 'pod install'\n",
|
|
8
|
+
default: ""
|
|
9
|
+
}) + "- You rebuilt the app after installing the package\n" + "- You are not using Expo Go\n");
|
|
10
|
+
}
|
|
11
|
+
const installed = NativeReactNativeSkiaVideo.install();
|
|
12
|
+
if (!installed || global.RNSkiaVideo == null) {
|
|
13
|
+
throw new Error("The package 'react-native-skia-webgpu-video' failed to install its JSI bindings.");
|
|
14
|
+
}
|
|
15
|
+
const module = global.RNSkiaVideo;
|
|
16
|
+
if (module.frameProtocolVersion !== 1) {
|
|
17
|
+
throw new Error("Skia 3 video native bindings are outdated. Rebuild the native app and remove the legacy Skia Video package.");
|
|
18
|
+
}
|
|
19
|
+
export default module;
|
|
20
|
+
//# sourceMappingURL=RNSkiaVideoModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Platform","NativeReactNativeSkiaVideo","Error","select","ios","default","installed","install","global","RNSkiaVideo","module","frameProtocolVersion"],"sourceRoot":"../../src","sources":["RNSkiaVideoModule.ts"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AAEvC,OAAOC,0BAA0B,MAAM,iCAA8B;AAErE,IAAI,CAACA,0BAA0B,EAAE;EAC/B,MAAM,IAAIC,KAAK,CACb,yFAAyF,GACvFF,QAAQ,CAACG,MAAM,CAAC;IAAEC,GAAG,EAAE,gCAAgC;IAAEC,OAAO,EAAE;EAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BACJ,CAAC;AACH;AAEA,MAAMC,SAAS,GAAGL,0BAA0B,CAACM,OAAO,CAAC,CAAC;AACtD,IAAI,CAACD,SAAS,IAAKE,MAAM,CAASC,WAAW,IAAI,IAAI,EAAE;EACrD,MAAM,IAAIP,KAAK,CACb,kFACF,CAAC;AACH;AAEA,MAAMQ,MAAM,GAAIF,MAAM,CAASC,WAAgC;AAC/D,IAAIC,MAAM,CAACC,oBAAoB,KAAK,CAAC,EAAE;EACrC,MAAM,IAAIT,KAAK,CACb,6GACF,CAAC;AACH;AACA,eAAeQ,MAAM","ignoreList":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** Fail early when JS was patched but the installed native binary was not rebuilt. */
|
|
4
|
+
export const getVideoCanvas = surface => {
|
|
5
|
+
"worklet";
|
|
6
|
+
|
|
7
|
+
const checked = surface;
|
|
8
|
+
if (typeof checked.enableCheckedSubmissions !== "function") {
|
|
9
|
+
throw new Error("Skia checked submission patch is missing from the native binary. Apply the patch and rebuild the native app.");
|
|
10
|
+
}
|
|
11
|
+
checked.enableCheckedSubmissions();
|
|
12
|
+
const canvas = surface.getCanvas();
|
|
13
|
+
if (typeof canvas.dispose !== "function") {
|
|
14
|
+
throw new Error("Skia canvas lifecycle patch is missing from the native binary. Apply the patch and rebuild the native app.");
|
|
15
|
+
}
|
|
16
|
+
return canvas;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getVideoCanvas","surface","checked","enableCheckedSubmissions","Error","canvas","getCanvas","dispose"],"sourceRoot":"../../src","sources":["canvas.ts"],"mappings":";;AAEA;AACA,OAAO,MAAMA,cAAc,GAAIC,OAAkB,IAAe;EAC9D,SAAS;;EACT,MAAMC,OAAO,GAAGD,OAEf;EACD,IAAI,OAAOC,OAAO,CAACC,wBAAwB,KAAK,UAAU,EAAE;IAC1D,MAAM,IAAIC,KAAK,CACb,8GACF,CAAC;EACH;EACAF,OAAO,CAACC,wBAAwB,CAAC,CAAC;EAClC,MAAME,MAAM,GAAGJ,OAAO,CAACK,SAAS,CAAC,CAAC;EAClC,IAAI,OAAOD,MAAM,CAACE,OAAO,KAAK,UAAU,EAAE;IACxC,MAAM,IAAIH,KAAK,CACb,4GACF,CAAC;EACH;EACA,OAAOC,MAAM;AACf,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Skia } from "react-native-skia";
|
|
4
|
+
|
|
5
|
+
/** Private contract, used only on this library's dedicated export runtime. */
|
|
6
|
+
export const assertExportRecorderCacheSupport = () => {
|
|
7
|
+
"worklet";
|
|
8
|
+
|
|
9
|
+
if (typeof Skia.__rnskvTrimRecorderCache !== "function") {
|
|
10
|
+
throw new Error("Skia export cache patch is missing from the native binary. Apply the patch and rebuild the native app.");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** Close all export consumers first. Never call this from preview/UI cleanup:
|
|
15
|
+
* that recorder can also cache resources for unrelated application views. */
|
|
16
|
+
export const trimExportRecorderCache = () => {
|
|
17
|
+
"worklet";
|
|
18
|
+
|
|
19
|
+
assertExportRecorderCacheSupport();
|
|
20
|
+
Skia.__rnskvTrimRecorderCache();
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=exportCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Skia","assertExportRecorderCacheSupport","__rnskvTrimRecorderCache","Error","trimExportRecorderCache"],"sourceRoot":"../../src","sources":["exportCache.ts"],"mappings":";;AAAA,SAASA,IAAI,QAAQ,mBAAmB;;AAExC;AACA,OAAO,MAAMC,gCAAgC,GAAGA,CAAA,KAAM;EACpD,SAAS;;EACT,IACE,OACED,IAAI,CAGJE,wBAAwB,KAAK,UAAU,EACzC;IACA,MAAM,IAAIC,KAAK,CACb,wGACF,CAAC;EACH;AACF,CAAC;;AAED;AACA;AACA,OAAO,MAAMC,uBAAuB,GAAGA,CAAA,KAAM;EAC3C,SAAS;;EACTH,gCAAgC,CAAC,CAAC;EAEhCD,IAAI,CACJE,wBAAwB,CAAC,CAAC;AAC9B,CAAC","ignoreList":[]}
|