@shopify/react-native-skia 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/android/CMakeLists.txt +1 -0
- package/android/cpp/jni/JniPlatformContext.cpp +23 -0
- package/android/cpp/jni/include/JniPlatformContext.h +2 -0
- package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +6 -0
- package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +92 -0
- package/android/cpp/rnskia-android/RNSkAndroidVideo.h +36 -0
- package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +34 -14
- package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +2 -1
- package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java +5 -0
- package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +185 -0
- package/cpp/api/JsiSkApi.h +2 -0
- package/cpp/api/JsiSkImageFactory.h +3 -3
- package/cpp/api/JsiVideo.h +87 -0
- package/cpp/rnskia/RNSkPlatformContext.h +4 -1
- package/cpp/rnskia/RNSkVideo.h +23 -0
- package/ios/RNSkia-iOS/RNSkiOSPlatformContext.h +2 -0
- package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +7 -1
- package/ios/RNSkia-iOS/RNSkiOSVideo.h +40 -0
- package/ios/RNSkia-iOS/RNSkiOSVideo.mm +119 -0
- package/ios/RNSkia-iOS/RNSkiOSView.mm +37 -0
- package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.mm +2 -2
- package/lib/commonjs/external/reanimated/index.d.ts +1 -0
- package/lib/commonjs/external/reanimated/index.js +11 -0
- package/lib/commonjs/external/reanimated/index.js.map +1 -1
- package/lib/commonjs/external/reanimated/useVideo.d.ts +10 -0
- package/lib/commonjs/external/reanimated/useVideo.js +93 -0
- package/lib/commonjs/external/reanimated/useVideo.js.map +1 -0
- package/lib/commonjs/mock/index.js +2 -1
- package/lib/commonjs/mock/index.js.map +1 -1
- package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +3 -3
- package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +2 -2
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
- package/lib/commonjs/skia/types/Skia.d.ts +2 -0
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/Video/Video.d.ts +8 -0
- package/lib/commonjs/skia/types/Video/Video.js +6 -0
- package/lib/commonjs/skia/types/Video/Video.js.map +1 -0
- package/lib/commonjs/skia/types/Video/index.d.ts +1 -0
- package/lib/commonjs/skia/types/Video/index.js +17 -0
- package/lib/commonjs/skia/types/Video/index.js.map +1 -0
- package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.d.ts +1 -1
- package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js +1 -1
- package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkia.js +4 -1
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/external/reanimated/index.d.ts +1 -0
- package/lib/module/external/reanimated/index.js +1 -0
- package/lib/module/external/reanimated/index.js.map +1 -1
- package/lib/module/external/reanimated/useVideo.d.ts +10 -0
- package/lib/module/external/reanimated/useVideo.js +85 -0
- package/lib/module/external/reanimated/useVideo.js.map +1 -0
- package/lib/module/mock/index.js +2 -1
- package/lib/module/mock/index.js.map +1 -1
- package/lib/module/skia/types/Image/ImageFactory.d.ts +3 -3
- package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +2 -2
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
- package/lib/module/skia/types/Skia.d.ts +2 -0
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/Video/Video.d.ts +8 -0
- package/lib/module/skia/types/Video/Video.js +2 -0
- package/lib/module/skia/types/Video/Video.js.map +1 -0
- package/lib/module/skia/types/Video/index.d.ts +1 -0
- package/lib/module/skia/types/Video/index.js +2 -0
- package/lib/module/skia/types/Video/index.js.map +1 -0
- package/lib/module/skia/web/JsiSkNativeBufferFactory.d.ts +1 -1
- package/lib/module/skia/web/JsiSkNativeBufferFactory.js +1 -1
- package/lib/module/skia/web/JsiSkNativeBufferFactory.js.map +1 -1
- package/lib/module/skia/web/JsiSkia.js +4 -1
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/typescript/src/external/reanimated/index.d.ts +1 -0
- package/lib/typescript/src/external/reanimated/useVideo.d.ts +10 -0
- package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +3 -3
- package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +2 -2
- package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
- package/lib/typescript/src/skia/types/Video/Video.d.ts +8 -0
- package/lib/typescript/src/skia/types/Video/index.d.ts +1 -0
- package/lib/typescript/src/skia/web/JsiSkNativeBufferFactory.d.ts +1 -1
- package/package.json +1 -1
- package/scripts/setup-canvaskit.js +1 -1
- package/src/external/reanimated/index.ts +1 -0
- package/src/external/reanimated/useVideo.ts +115 -0
- package/src/mock/index.ts +1 -0
- package/src/skia/types/Image/ImageFactory.ts +3 -3
- package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +2 -2
- package/src/skia/types/Skia.ts +2 -0
- package/src/skia/types/Video/Video.ts +9 -0
- package/src/skia/types/Video/index.ts +1 -0
- package/src/skia/web/JsiSkNativeBufferFactory.ts +1 -1
- package/src/skia/web/JsiSkia.ts +3 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
import {
|
2
|
+
runOnUI,
|
3
|
+
useSharedValue,
|
4
|
+
type FrameInfo,
|
5
|
+
type SharedValue,
|
6
|
+
} from "react-native-reanimated";
|
7
|
+
import { useCallback, useEffect, useMemo } from "react";
|
8
|
+
|
9
|
+
import { Skia } from "../../skia/Skia";
|
10
|
+
import type { SkImage } from "../../skia/types";
|
11
|
+
import { Platform } from "../../Platform";
|
12
|
+
|
13
|
+
import Rea from "./ReanimatedProxy";
|
14
|
+
|
15
|
+
type Animated<T> = SharedValue<T> | T;
|
16
|
+
|
17
|
+
export interface PlaybackOptions {
|
18
|
+
playbackSpeed: Animated<number>;
|
19
|
+
looping: Animated<boolean>;
|
20
|
+
paused: Animated<boolean>;
|
21
|
+
}
|
22
|
+
|
23
|
+
const defaultOptions = {
|
24
|
+
playbackSpeed: 1,
|
25
|
+
looping: true,
|
26
|
+
paused: false,
|
27
|
+
};
|
28
|
+
|
29
|
+
const useOption = <T>(value: Animated<T>) => {
|
30
|
+
"worklet";
|
31
|
+
// TODO: only create defaultValue is needed (via makeMutable)
|
32
|
+
const defaultValue = useSharedValue(
|
33
|
+
Rea.isSharedValue(value) ? value.value : value
|
34
|
+
);
|
35
|
+
return Rea.isSharedValue(value) ? value : defaultValue;
|
36
|
+
};
|
37
|
+
|
38
|
+
export const useVideo = (
|
39
|
+
source: string | null,
|
40
|
+
userOptions?: Partial<PlaybackOptions>
|
41
|
+
) => {
|
42
|
+
const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);
|
43
|
+
const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);
|
44
|
+
const looping = useOption(userOptions?.looping ?? defaultOptions.looping);
|
45
|
+
const playbackSpeed = useOption(
|
46
|
+
userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed
|
47
|
+
);
|
48
|
+
const currentFrame = Rea.useSharedValue<null | SkImage>(null);
|
49
|
+
const lastTimestamp = Rea.useSharedValue(-1);
|
50
|
+
const startTimestamp = Rea.useSharedValue(-1);
|
51
|
+
|
52
|
+
const framerate = useMemo(() => (video ? video.framerate() : -1), [video]);
|
53
|
+
const duration = useMemo(() => (video ? video.duration() : -1), [video]);
|
54
|
+
const frameDuration = useMemo(
|
55
|
+
() => (framerate > 0 ? 1000 / framerate : -1),
|
56
|
+
[framerate]
|
57
|
+
);
|
58
|
+
const disposeVideo = useCallback(() => {
|
59
|
+
"worklet";
|
60
|
+
video?.dispose();
|
61
|
+
}, [video]);
|
62
|
+
|
63
|
+
Rea.useFrameCallback((frameInfo: FrameInfo) => {
|
64
|
+
if (!video) {
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
if (isPaused.value && lastTimestamp.value !== -1) {
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
const { timestamp } = frameInfo;
|
71
|
+
|
72
|
+
// Initialize start timestamp
|
73
|
+
if (startTimestamp.value === -1) {
|
74
|
+
startTimestamp.value = timestamp;
|
75
|
+
}
|
76
|
+
|
77
|
+
// Calculate the current time in the video
|
78
|
+
const currentTimestamp = timestamp - startTimestamp.value;
|
79
|
+
|
80
|
+
// Handle looping
|
81
|
+
if (currentTimestamp > duration && looping.value) {
|
82
|
+
video.seek(0);
|
83
|
+
startTimestamp.value = timestamp;
|
84
|
+
}
|
85
|
+
|
86
|
+
// Update frame only if the elapsed time since last update is greater than the frame duration
|
87
|
+
const currentFrameDuration = frameDuration / playbackSpeed.value;
|
88
|
+
if (
|
89
|
+
lastTimestamp.value === -1 ||
|
90
|
+
timestamp - lastTimestamp.value >= currentFrameDuration
|
91
|
+
) {
|
92
|
+
const img = video.nextImage();
|
93
|
+
if (img) {
|
94
|
+
if (currentFrame.value) {
|
95
|
+
currentFrame.value.dispose();
|
96
|
+
}
|
97
|
+
if (Platform.OS === "android") {
|
98
|
+
currentFrame.value = img.makeNonTextureImage();
|
99
|
+
} else {
|
100
|
+
currentFrame.value = img;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
lastTimestamp.value = timestamp;
|
104
|
+
}
|
105
|
+
});
|
106
|
+
|
107
|
+
useEffect(() => {
|
108
|
+
return () => {
|
109
|
+
// TODO: should video simply be a shared value instead?
|
110
|
+
runOnUI(disposeVideo)();
|
111
|
+
};
|
112
|
+
}, [disposeVideo, video]);
|
113
|
+
|
114
|
+
return currentFrame;
|
115
|
+
};
|
package/src/mock/index.ts
CHANGED
@@ -54,7 +54,7 @@ export interface ImageFactory {
|
|
54
54
|
|
55
55
|
/**
|
56
56
|
* Return an Image backed by a given native buffer.
|
57
|
-
* The
|
57
|
+
* The native buffer must be a valid owning reference.
|
58
58
|
*
|
59
59
|
* For instance, this API is used by
|
60
60
|
* [react-native-vision-camera](https://github.com/mrousavy/react-native-vision-camera)
|
@@ -62,9 +62,9 @@ export interface ImageFactory {
|
|
62
62
|
*
|
63
63
|
* - On Android; This is an `AHardwareBuffer*`
|
64
64
|
* - On iOS, this is a `CVPixelBufferRef`
|
65
|
-
* @param nativeBuffer A strong `uintptr_t` pointer to the native
|
65
|
+
* @param nativeBuffer A strong `uintptr_t` pointer to the native buffer
|
66
66
|
* @throws Throws an error if the Image could not be created, for example when the given
|
67
|
-
*
|
67
|
+
* native buffer is invalid.
|
68
68
|
*/
|
69
69
|
MakeImageFromNativeBuffer: (nativeBuffer: NativeBuffer) => SkImage;
|
70
70
|
|
@@ -32,7 +32,7 @@ export interface NativeBufferFactory {
|
|
32
32
|
*/
|
33
33
|
MakeFromImage: (image: SkImage) => NativeBuffer;
|
34
34
|
/**
|
35
|
-
* Release a
|
35
|
+
* Release a native buffer that was created with `MakeFromImage`.
|
36
36
|
*/
|
37
|
-
Release: (
|
37
|
+
Release: (nativeBuffer: NativeBuffer) => void;
|
38
38
|
}
|
package/src/skia/types/Skia.ts
CHANGED
@@ -30,6 +30,7 @@ import type { Color, SkColor } from "./Color";
|
|
30
30
|
import type { TypefaceFontProviderFactory } from "./Paragraph/TypefaceFontProviderFactory";
|
31
31
|
import type { AnimatedImageFactory } from "./AnimatedImage";
|
32
32
|
import type { ParagraphBuilderFactory } from "./Paragraph/ParagraphBuilder";
|
33
|
+
import type { Video } from "./Video";
|
33
34
|
import type { NativeBufferFactory } from "./NativeBuffer";
|
34
35
|
|
35
36
|
/**
|
@@ -95,5 +96,6 @@ export interface Skia {
|
|
95
96
|
TextBlob: TextBlobFactory;
|
96
97
|
Surface: SurfaceFactory;
|
97
98
|
ParagraphBuilder: ParagraphBuilderFactory;
|
99
|
+
Video: (url: string) => Video;
|
98
100
|
NativeBuffer: NativeBufferFactory;
|
99
101
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./Video";
|
package/src/skia/web/JsiSkia.ts
CHANGED
@@ -127,4 +127,7 @@ export const JsiSkApi = (CanvasKit: CanvasKit): Skia => ({
|
|
127
127
|
FontMgr: new JsiSkFontMgrFactory(CanvasKit),
|
128
128
|
ParagraphBuilder: new JsiSkParagraphBuilderFactory(CanvasKit),
|
129
129
|
NativeBuffer: new JsiSkNativeBufferFactory(CanvasKit),
|
130
|
+
Video: (_localUri: string) => {
|
131
|
+
throw new Error("Not implemented on React Native Web");
|
132
|
+
},
|
130
133
|
});
|