@shopify/react-native-skia 2.4.17 → 2.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +34 -0
- package/android/cpp/rnskia-android/RNSkAndroidVideo.h +3 -0
- package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +72 -18
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +7 -7
- package/apple/RNSkAppleVideo.h +30 -3
- package/apple/RNSkAppleVideo.mm +172 -17
- package/cpp/api/JsiSkApi.h +15 -13
- package/cpp/api/JsiSkHostObjects.h +57 -3
- package/cpp/api/JsiSkImage.h +19 -5
- package/cpp/api/JsiSkPicture.h +19 -5
- package/cpp/api/JsiSkSurface.h +19 -5
- package/cpp/api/JsiVideo.h +15 -2
- package/cpp/api/recorder/Convertor.h +4 -2
- package/cpp/jsi2/EnumMapper.h +49 -34
- package/cpp/jsi2/JSIConverter.h +149 -99
- package/cpp/jsi2/NativeObject.h +23 -25
- package/cpp/jsi2/Promise.cpp +10 -6
- package/cpp/jsi2/Promise.h +9 -7
- package/cpp/rnskia/RNDawnContext.h +3 -8
- package/cpp/rnskia/RNSkManager.cpp +13 -7
- package/cpp/rnskia/RNSkVideo.h +3 -0
- package/cpp/rnwgpu/api/GPUAdapter.cpp +31 -32
- package/cpp/rnwgpu/api/GPUAdapter.h +1 -1
- package/cpp/rnwgpu/api/GPUBuffer.cpp +8 -8
- package/cpp/rnwgpu/api/GPUCommandEncoder.h +4 -4
- package/cpp/rnwgpu/api/GPUDevice.h +12 -12
- package/cpp/rnwgpu/api/GPUQueue.cpp +45 -44
- package/cpp/rnwgpu/api/GPUQueue.h +1 -1
- package/cpp/rnwgpu/api/GPURenderBundleEncoder.h +1 -1
- package/cpp/rnwgpu/api/GPURenderPassEncoder.h +1 -1
- package/cpp/rnwgpu/api/descriptors/GPUBindGroupEntry.h +1 -1
- package/cpp/rnwgpu/api/descriptors/GPUComputePipelineDescriptor.h +1 -1
- package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +7 -6
- package/cpp/rnwgpu/api/descriptors/GPURenderPassDescriptor.h +1 -1
- package/cpp/rnwgpu/api/descriptors/GPURenderPipelineDescriptor.h +1 -1
- package/cpp/rnwgpu/api/descriptors/GPUVertexState.h +1 -1
- package/cpp/rnwgpu/async/AsyncRunner.cpp +2 -1
- package/cpp/rnwgpu/async/AsyncRunner.h +2 -1
- package/lib/commonjs/external/reanimated/useVideo.js +30 -31
- package/lib/commonjs/external/reanimated/useVideo.js.map +1 -1
- package/lib/commonjs/renderer/Offscreen.js +1 -0
- package/lib/commonjs/renderer/Offscreen.js.map +1 -1
- package/lib/commonjs/skia/types/Video/Video.d.ts +3 -0
- package/lib/commonjs/skia/types/Video/Video.js.map +1 -1
- package/lib/commonjs/skia/web/JsiVideo.d.ts +3 -0
- package/lib/commonjs/skia/web/JsiVideo.js +9 -0
- package/lib/commonjs/skia/web/JsiVideo.js.map +1 -1
- package/lib/commonjs/sksg/Container.web.js +1 -0
- package/lib/commonjs/sksg/Container.web.js.map +1 -1
- package/lib/commonjs/sksg/HostConfig.js +4 -1
- package/lib/commonjs/sksg/HostConfig.js.map +1 -1
- package/lib/commonjs/sksg/Reconciler.js +6 -6
- package/lib/commonjs/sksg/Reconciler.js.map +1 -1
- package/lib/commonjs/sksg/StaticContainer.js +1 -0
- package/lib/commonjs/sksg/StaticContainer.js.map +1 -1
- package/lib/module/external/reanimated/useVideo.js +30 -31
- package/lib/module/external/reanimated/useVideo.js.map +1 -1
- package/lib/module/renderer/Offscreen.js +1 -0
- package/lib/module/renderer/Offscreen.js.map +1 -1
- package/lib/module/skia/types/Video/Video.d.ts +3 -0
- package/lib/module/skia/types/Video/Video.js.map +1 -1
- package/lib/module/skia/web/JsiVideo.d.ts +3 -0
- package/lib/module/skia/web/JsiVideo.js +9 -0
- package/lib/module/skia/web/JsiVideo.js.map +1 -1
- package/lib/module/sksg/Container.web.js +1 -0
- package/lib/module/sksg/Container.web.js.map +1 -1
- package/lib/module/sksg/HostConfig.js +4 -1
- package/lib/module/sksg/HostConfig.js.map +1 -1
- package/lib/module/sksg/Reconciler.js +6 -6
- package/lib/module/sksg/Reconciler.js.map +1 -1
- package/lib/module/sksg/StaticContainer.js +1 -0
- package/lib/module/sksg/StaticContainer.js.map +1 -1
- package/lib/typescript/lib/commonjs/skia/web/JsiVideo.d.ts +3 -0
- package/lib/typescript/lib/commonjs/sksg/HostConfig.d.ts +2 -0
- package/lib/typescript/lib/module/skia/web/JsiVideo.d.ts +3 -0
- package/lib/typescript/lib/module/sksg/HostConfig.d.ts +2 -0
- package/lib/typescript/src/skia/types/Video/Video.d.ts +3 -0
- package/lib/typescript/src/skia/web/JsiVideo.d.ts +3 -0
- package/package.json +1 -1
- package/src/external/reanimated/useVideo.ts +32 -32
- package/src/renderer/Offscreen.tsx +1 -0
- package/src/skia/types/Video/Video.ts +3 -0
- package/src/skia/web/JsiVideo.ts +12 -0
- package/src/sksg/Container.web.ts +1 -0
- package/src/sksg/HostConfig.ts +4 -0
- package/src/sksg/Reconciler.ts +5 -6
- package/src/sksg/StaticContainer.ts +1 -0
|
@@ -44,13 +44,11 @@ const defaultOptions = {
|
|
|
44
44
|
looping: true,
|
|
45
45
|
paused: false,
|
|
46
46
|
seek: null,
|
|
47
|
-
currentTime: 0,
|
|
48
47
|
volume: 0,
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
const useOption = <T>(value: MaybeAnimated<T>) => {
|
|
52
51
|
"worklet";
|
|
53
|
-
// TODO: only create defaultValue is needed (via makeMutable)
|
|
54
52
|
const defaultValue = Rea.useSharedValue(
|
|
55
53
|
Rea.isSharedValue(value) ? value.value : value
|
|
56
54
|
);
|
|
@@ -75,7 +73,6 @@ export const useVideo = (
|
|
|
75
73
|
const volume = useOption(userOptions?.volume ?? defaultOptions.volume);
|
|
76
74
|
const currentFrame = Rea.useSharedValue<null | SkImage>(null);
|
|
77
75
|
const currentTime = Rea.useSharedValue(0);
|
|
78
|
-
const lastTimestamp = Rea.useSharedValue(-1);
|
|
79
76
|
const duration = useMemo(() => video?.duration() ?? 0, [video]);
|
|
80
77
|
const framerate = useMemo(
|
|
81
78
|
() => (Platform.OS === "web" ? -1 : (video?.framerate() ?? 0)),
|
|
@@ -83,70 +80,73 @@ export const useVideo = (
|
|
|
83
80
|
);
|
|
84
81
|
const size = useMemo(() => video?.size() ?? { width: 0, height: 0 }, [video]);
|
|
85
82
|
const rotation = useMemo(() => video?.rotation() ?? 0, [video]);
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
|
|
84
|
+
// Handle pause/play state changes
|
|
88
85
|
Rea.useAnimatedReaction(
|
|
89
86
|
() => isPaused.value,
|
|
90
87
|
(paused) => {
|
|
91
88
|
if (paused) {
|
|
92
89
|
video?.pause();
|
|
93
90
|
} else {
|
|
94
|
-
lastTimestamp.value = -1;
|
|
95
91
|
video?.play();
|
|
96
92
|
}
|
|
97
93
|
}
|
|
98
94
|
);
|
|
95
|
+
|
|
96
|
+
// Handle seek
|
|
99
97
|
Rea.useAnimatedReaction(
|
|
100
98
|
() => seek.value,
|
|
101
99
|
(value) => {
|
|
102
100
|
if (value !== null) {
|
|
103
101
|
video?.seek(value);
|
|
104
|
-
currentTime.value = value;
|
|
105
102
|
seek.value = null;
|
|
106
103
|
}
|
|
107
104
|
}
|
|
108
105
|
);
|
|
106
|
+
|
|
107
|
+
// Handle volume changes
|
|
109
108
|
Rea.useAnimatedReaction(
|
|
110
109
|
() => volume.value,
|
|
111
110
|
(value) => {
|
|
112
111
|
video?.setVolume(value);
|
|
113
112
|
}
|
|
114
113
|
);
|
|
115
|
-
|
|
114
|
+
|
|
115
|
+
// Handle looping changes
|
|
116
|
+
Rea.useAnimatedReaction(
|
|
117
|
+
() => looping.value,
|
|
118
|
+
(value) => {
|
|
119
|
+
video?.setLooping(value);
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Frame callback - simplified since native handles frame timing
|
|
124
|
+
Rea.useFrameCallback((_frameInfo: FrameInfo) => {
|
|
116
125
|
"worklet";
|
|
117
126
|
if (!video) {
|
|
118
127
|
return;
|
|
119
128
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (lastTimestamp.value === -1) {
|
|
125
|
-
lastTimestamp.value = currentTimestamp;
|
|
126
|
-
}
|
|
127
|
-
const delta = currentTimestamp - lastTimestamp.value;
|
|
128
|
-
|
|
129
|
-
const isOver = currentTime.value + delta > duration;
|
|
130
|
-
if (isOver && looping.value) {
|
|
131
|
-
seek.value = 0;
|
|
132
|
-
currentTime.value = seek.value;
|
|
133
|
-
lastTimestamp.value = currentTimestamp;
|
|
134
|
-
}
|
|
135
|
-
// On Web the framerate is uknown.
|
|
136
|
-
// This could be optimized by using requestVideoFrameCallback (Chrome only)
|
|
137
|
-
if ((delta >= currentFrameDuration && !isOver) || Platform.OS === "web") {
|
|
138
|
-
setFrame(video, currentFrame);
|
|
139
|
-
currentTime.value += delta;
|
|
140
|
-
lastTimestamp.value = currentTimestamp;
|
|
141
|
-
}
|
|
129
|
+
// Update current time from native player
|
|
130
|
+
currentTime.value = video.currentTime();
|
|
131
|
+
// Get the latest frame (native handles timing via CADisplayLink/etc)
|
|
132
|
+
setFrame(video, currentFrame);
|
|
142
133
|
});
|
|
143
134
|
|
|
135
|
+
// Apply initial state when video becomes available
|
|
144
136
|
useEffect(() => {
|
|
137
|
+
if (video) {
|
|
138
|
+
video.setLooping(looping.value);
|
|
139
|
+
video.setVolume(volume.value);
|
|
140
|
+
if (isPaused.value) {
|
|
141
|
+
video.pause();
|
|
142
|
+
} else {
|
|
143
|
+
video.play();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
145
146
|
return () => {
|
|
146
|
-
// TODO: should video simply be a shared value instead?
|
|
147
147
|
Rea.runOnUI(disposeVideo)(video);
|
|
148
148
|
};
|
|
149
|
-
}, [video]);
|
|
149
|
+
}, [video, isPaused, looping, volume]);
|
|
150
150
|
|
|
151
151
|
return {
|
|
152
152
|
currentFrame,
|
|
@@ -20,6 +20,7 @@ export const drawAsPicture = async (element: ReactElement, bounds?: SkRect) => {
|
|
|
20
20
|
await root.render(element);
|
|
21
21
|
root.drawOnCanvas(canvas);
|
|
22
22
|
const picture = recorder.finishRecordingAsPicture();
|
|
23
|
+
recorder.dispose();
|
|
23
24
|
root.unmount();
|
|
24
25
|
return picture;
|
|
25
26
|
};
|
|
@@ -6,6 +6,7 @@ export type VideoRotation = 0 | 90 | 180 | 270;
|
|
|
6
6
|
export interface Video extends SkJSIInstance<"Video"> {
|
|
7
7
|
duration(): number;
|
|
8
8
|
framerate(): number;
|
|
9
|
+
currentTime(): number;
|
|
9
10
|
nextImage(): SkImage | null;
|
|
10
11
|
seek(time: number): void;
|
|
11
12
|
rotation(): VideoRotation;
|
|
@@ -13,4 +14,6 @@ export interface Video extends SkJSIInstance<"Video"> {
|
|
|
13
14
|
pause(): void;
|
|
14
15
|
play(): void;
|
|
15
16
|
setVolume(volume: number): void;
|
|
17
|
+
setLooping(looping: boolean): void;
|
|
18
|
+
isPlaying(): boolean;
|
|
16
19
|
}
|
package/src/skia/web/JsiVideo.ts
CHANGED
|
@@ -46,6 +46,10 @@ export class JsiVideo implements Video {
|
|
|
46
46
|
return throwNotImplementedOnRNWeb<number>();
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
currentTime() {
|
|
50
|
+
return this.videoElement.currentTime * 1000;
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
setSurface(surface: Surface) {
|
|
50
54
|
// If we have the surface, we can use the WebGL buffer which is slightly faster
|
|
51
55
|
// This is because WebGL cannot be shared across contextes.
|
|
@@ -89,6 +93,14 @@ export class JsiVideo implements Video {
|
|
|
89
93
|
this.videoElement.volume = volume;
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
setLooping(looping: boolean) {
|
|
97
|
+
this.videoElement.loop = looping;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
isPlaying() {
|
|
101
|
+
return !this.videoElement.paused && !this.videoElement.ended;
|
|
102
|
+
}
|
|
103
|
+
|
|
92
104
|
[Symbol.dispose]() {
|
|
93
105
|
if (this.videoElement.parentNode) {
|
|
94
106
|
this.videoElement.parentNode.removeChild(this.videoElement);
|
|
@@ -21,6 +21,7 @@ const drawOnscreen = (Skia: Skia, nativeId: number, recording: Recording) => {
|
|
|
21
21
|
const ctx = createDrawingContext(Skia, recording.paintPool, canvas);
|
|
22
22
|
replay(ctx, recording.commands);
|
|
23
23
|
const picture = rec.finishRecordingAsPicture();
|
|
24
|
+
rec.dispose();
|
|
24
25
|
//const end = performance.now();
|
|
25
26
|
//console.log("Recording time: ", end - start);
|
|
26
27
|
SkiaViewApi.setJsiProperty(nativeId, "picture", picture);
|
package/src/sksg/HostConfig.ts
CHANGED
|
@@ -263,4 +263,8 @@ export const sksgHostConfig: SkiaHostConfig = {
|
|
|
263
263
|
return DefaultEventPriority;
|
|
264
264
|
},
|
|
265
265
|
resetFormInstance() {},
|
|
266
|
+
|
|
267
|
+
// DefinitelyTyped is not up to date, these are needed for devtools
|
|
268
|
+
rendererVersion: "0.0.1",
|
|
269
|
+
rendererPackageName: "react-native-skia",
|
|
266
270
|
};
|
package/src/sksg/Reconciler.ts
CHANGED
|
@@ -13,11 +13,8 @@ import "./Elements";
|
|
|
13
13
|
|
|
14
14
|
const skiaReconciler = ReactReconciler(sksgHostConfig);
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
version: "0.0.1",
|
|
19
|
-
rendererPackageName: "react-native-skia",
|
|
20
|
-
});
|
|
16
|
+
// @ts-expect-error DefinitelyTyped is not up to date
|
|
17
|
+
skiaReconciler.injectIntoDevTools();
|
|
21
18
|
|
|
22
19
|
export class SkiaSGRoot {
|
|
23
20
|
private root: OpaqueRoot;
|
|
@@ -68,7 +65,9 @@ export class SkiaSGRoot {
|
|
|
68
65
|
const recorder = this.Skia.PictureRecorder();
|
|
69
66
|
const canvas = recorder.beginRecording();
|
|
70
67
|
this.drawOnCanvas(canvas);
|
|
71
|
-
|
|
68
|
+
const picture = recorder.finishRecordingAsPicture();
|
|
69
|
+
recorder.dispose();
|
|
70
|
+
return picture;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
unmount() {
|
|
@@ -65,6 +65,7 @@ export class StaticContainer extends Container {
|
|
|
65
65
|
const canvas = rec.beginRecording();
|
|
66
66
|
this.drawOnCanvas(canvas);
|
|
67
67
|
const picture = rec.finishRecordingAsPicture();
|
|
68
|
+
rec.dispose();
|
|
68
69
|
SkiaViewApi.setJsiProperty(this.nativeId, "picture", picture);
|
|
69
70
|
}
|
|
70
71
|
}
|