@shopify/react-native-skia 1.3.0 → 1.3.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.
@@ -5,6 +5,8 @@ export interface PlaybackOptions {
5
5
  playbackSpeed: Animated<number>;
6
6
  looping: Animated<boolean>;
7
7
  paused: Animated<boolean>;
8
+ seek: Animated<number | null>;
9
+ currentTime: Animated<number>;
8
10
  }
9
11
  export declare const useVideo: (source: string | null, userOptions?: Partial<PlaybackOptions>) => SharedValue<SkImage | null>;
10
12
  export {};
@@ -13,7 +13,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
13
  const defaultOptions = {
14
14
  playbackSpeed: 1,
15
15
  looping: true,
16
- paused: false
16
+ paused: false,
17
+ seek: null,
18
+ currentTime: 0
17
19
  };
18
20
  const useOption = value => {
19
21
  "worklet";
@@ -23,10 +25,12 @@ const useOption = value => {
23
25
  return _ReanimatedProxy.default.isSharedValue(value) ? value : defaultValue;
24
26
  };
25
27
  const useVideo = (source, userOptions) => {
26
- var _userOptions$paused, _userOptions$looping, _userOptions$playback;
28
+ var _userOptions$paused, _userOptions$looping, _userOptions$seek, _userOptions$currentT, _userOptions$playback;
27
29
  const video = (0, _react.useMemo)(() => source ? _Skia.Skia.Video(source) : null, [source]);
28
30
  const isPaused = useOption((_userOptions$paused = userOptions === null || userOptions === void 0 ? void 0 : userOptions.paused) !== null && _userOptions$paused !== void 0 ? _userOptions$paused : defaultOptions.paused);
29
31
  const looping = useOption((_userOptions$looping = userOptions === null || userOptions === void 0 ? void 0 : userOptions.looping) !== null && _userOptions$looping !== void 0 ? _userOptions$looping : defaultOptions.looping);
32
+ const seek = useOption((_userOptions$seek = userOptions === null || userOptions === void 0 ? void 0 : userOptions.seek) !== null && _userOptions$seek !== void 0 ? _userOptions$seek : defaultOptions.seek);
33
+ const currentTime = useOption((_userOptions$currentT = userOptions === null || userOptions === void 0 ? void 0 : userOptions.currentTime) !== null && _userOptions$currentT !== void 0 ? _userOptions$currentT : defaultOptions.currentTime);
30
34
  const playbackSpeed = useOption((_userOptions$playback = userOptions === null || userOptions === void 0 ? void 0 : userOptions.playbackSpeed) !== null && _userOptions$playback !== void 0 ? _userOptions$playback : defaultOptions.playbackSpeed);
31
35
  const currentFrame = _ReanimatedProxy.default.useSharedValue(null);
32
36
  const lastTimestamp = _ReanimatedProxy.default.useSharedValue(-1);
@@ -43,6 +47,12 @@ const useVideo = (source, userOptions) => {
43
47
  if (!video) {
44
48
  return;
45
49
  }
50
+ if (seek.value !== null) {
51
+ video.seek(seek.value);
52
+ seek.value = null;
53
+ lastTimestamp.value = -1;
54
+ startTimestamp.value = -1;
55
+ }
46
56
  if (isPaused.value && lastTimestamp.value !== -1) {
47
57
  return;
48
58
  }
@@ -57,6 +67,7 @@ const useVideo = (source, userOptions) => {
57
67
 
58
68
  // Calculate the current time in the video
59
69
  const currentTimestamp = timestamp - startTimestamp.value;
70
+ currentTime.value = currentTimestamp;
60
71
 
61
72
  // Handle looping
62
73
  if (currentTimestamp > duration && looping.value) {
@@ -65,8 +76,9 @@ const useVideo = (source, userOptions) => {
65
76
  }
66
77
 
67
78
  // Update frame only if the elapsed time since last update is greater than the frame duration
68
- const currentFrameDuration = frameDuration / playbackSpeed.value;
69
- if (lastTimestamp.value === -1 || timestamp - lastTimestamp.value >= currentFrameDuration) {
79
+ const currentFrameDuration = Math.floor(frameDuration / playbackSpeed.value);
80
+ const delta = Math.floor(timestamp - lastTimestamp.value);
81
+ if (lastTimestamp.value === -1 || delta >= currentFrameDuration) {
70
82
  const img = video.nextImage();
71
83
  if (img) {
72
84
  if (currentFrame.value) {
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNativeReanimated","require","_react","_Skia","_Platform","_ReanimatedProxy","_interopRequireDefault","obj","__esModule","default","defaultOptions","playbackSpeed","looping","paused","useOption","value","defaultValue","useSharedValue","Rea","isSharedValue","useVideo","source","userOptions","_userOptions$paused","_userOptions$looping","_userOptions$playback","video","useMemo","Skia","Video","isPaused","currentFrame","lastTimestamp","startTimestamp","framerate","duration","frameDuration","disposeVideo","useCallback","dispose","useFrameCallback","frameInfo","timestamp","currentTimestamp","seek","currentFrameDuration","img","nextImage","Platform","OS","makeNonTextureImage","useEffect","runOnUI","exports"],"sources":["useVideo.ts"],"sourcesContent":["import {\n runOnUI,\n useSharedValue,\n type FrameInfo,\n type SharedValue,\n} from \"react-native-reanimated\";\nimport { useCallback, useEffect, useMemo } from \"react\";\n\nimport { Skia } from \"../../skia/Skia\";\nimport type { SkImage } from \"../../skia/types\";\nimport { Platform } from \"../../Platform\";\n\nimport Rea from \"./ReanimatedProxy\";\n\ntype Animated<T> = SharedValue<T> | T;\n\nexport interface PlaybackOptions {\n playbackSpeed: Animated<number>;\n looping: Animated<boolean>;\n paused: Animated<boolean>;\n}\n\nconst defaultOptions = {\n playbackSpeed: 1,\n looping: true,\n paused: false,\n};\n\nconst useOption = <T>(value: Animated<T>) => {\n \"worklet\";\n // TODO: only create defaultValue is needed (via makeMutable)\n const defaultValue = useSharedValue(\n Rea.isSharedValue(value) ? value.value : value\n );\n return Rea.isSharedValue(value) ? value : defaultValue;\n};\n\nexport const useVideo = (\n source: string | null,\n userOptions?: Partial<PlaybackOptions>\n) => {\n const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);\n const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);\n const looping = useOption(userOptions?.looping ?? defaultOptions.looping);\n const playbackSpeed = useOption(\n userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed\n );\n const currentFrame = Rea.useSharedValue<null | SkImage>(null);\n const lastTimestamp = Rea.useSharedValue(-1);\n const startTimestamp = Rea.useSharedValue(-1);\n\n const framerate = useMemo(() => (video ? video.framerate() : -1), [video]);\n const duration = useMemo(() => (video ? video.duration() : -1), [video]);\n const frameDuration = useMemo(\n () => (framerate > 0 ? 1000 / framerate : -1),\n [framerate]\n );\n const disposeVideo = useCallback(() => {\n \"worklet\";\n video?.dispose();\n }, [video]);\n\n Rea.useFrameCallback((frameInfo: FrameInfo) => {\n if (!video) {\n return;\n }\n if (isPaused.value && lastTimestamp.value !== -1) {\n return;\n }\n const { timestamp } = frameInfo;\n\n // Initialize start timestamp\n if (startTimestamp.value === -1) {\n startTimestamp.value = timestamp;\n }\n\n // Calculate the current time in the video\n const currentTimestamp = timestamp - startTimestamp.value;\n\n // Handle looping\n if (currentTimestamp > duration && looping.value) {\n video.seek(0);\n startTimestamp.value = timestamp;\n }\n\n // Update frame only if the elapsed time since last update is greater than the frame duration\n const currentFrameDuration = frameDuration / playbackSpeed.value;\n if (\n lastTimestamp.value === -1 ||\n timestamp - lastTimestamp.value >= currentFrameDuration\n ) {\n const img = video.nextImage();\n if (img) {\n if (currentFrame.value) {\n currentFrame.value.dispose();\n }\n if (Platform.OS === \"android\") {\n currentFrame.value = img.makeNonTextureImage();\n } else {\n currentFrame.value = img;\n }\n }\n lastTimestamp.value = timestamp;\n }\n });\n\n useEffect(() => {\n return () => {\n // TODO: should video simply be a shared value instead?\n runOnUI(disposeVideo)();\n };\n }, [disposeVideo, video]);\n\n return currentFrame;\n};\n"],"mappings":";;;;;;AAAA,IAAAA,sBAAA,GAAAC,OAAA;AAMA,IAAAC,MAAA,GAAAD,OAAA;AAEA,IAAAE,KAAA,GAAAF,OAAA;AAEA,IAAAG,SAAA,GAAAH,OAAA;AAEA,IAAAI,gBAAA,GAAAC,sBAAA,CAAAL,OAAA;AAAoC,SAAAK,uBAAAC,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAUpC,MAAMG,cAAc,GAAG;EACrBC,aAAa,EAAE,CAAC;EAChBC,OAAO,EAAE,IAAI;EACbC,MAAM,EAAE;AACV,CAAC;AAED,MAAMC,SAAS,GAAOC,KAAkB,IAAK;EAC3C,SAAS;;EACT;EACA,MAAMC,YAAY,GAAG,IAAAC,qCAAc,EACjCC,wBAAG,CAACC,aAAa,CAACJ,KAAK,CAAC,GAAGA,KAAK,CAACA,KAAK,GAAGA,KAC3C,CAAC;EACD,OAAOG,wBAAG,CAACC,aAAa,CAACJ,KAAK,CAAC,GAAGA,KAAK,GAAGC,YAAY;AACxD,CAAC;AAEM,MAAMI,QAAQ,GAAGA,CACtBC,MAAqB,EACrBC,WAAsC,KACnC;EAAA,IAAAC,mBAAA,EAAAC,oBAAA,EAAAC,qBAAA;EACH,MAAMC,KAAK,GAAG,IAAAC,cAAO,EAAC,MAAON,MAAM,GAAGO,UAAI,CAACC,KAAK,CAACR,MAAM,CAAC,GAAG,IAAK,EAAE,CAACA,MAAM,CAAC,CAAC;EAC3E,MAAMS,QAAQ,GAAGhB,SAAS,EAAAS,mBAAA,GAACD,WAAW,aAAXA,WAAW,uBAAXA,WAAW,CAAET,MAAM,cAAAU,mBAAA,cAAAA,mBAAA,GAAIb,cAAc,CAACG,MAAM,CAAC;EACxE,MAAMD,OAAO,GAAGE,SAAS,EAAAU,oBAAA,GAACF,WAAW,aAAXA,WAAW,uBAAXA,WAAW,CAAEV,OAAO,cAAAY,oBAAA,cAAAA,oBAAA,GAAId,cAAc,CAACE,OAAO,CAAC;EACzE,MAAMD,aAAa,GAAGG,SAAS,EAAAW,qBAAA,GAC7BH,WAAW,aAAXA,WAAW,uBAAXA,WAAW,CAAEX,aAAa,cAAAc,qBAAA,cAAAA,qBAAA,GAAIf,cAAc,CAACC,aAC/C,CAAC;EACD,MAAMoB,YAAY,GAAGb,wBAAG,CAACD,cAAc,CAAiB,IAAI,CAAC;EAC7D,MAAMe,aAAa,GAAGd,wBAAG,CAACD,cAAc,CAAC,CAAC,CAAC,CAAC;EAC5C,MAAMgB,cAAc,GAAGf,wBAAG,CAACD,cAAc,CAAC,CAAC,CAAC,CAAC;EAE7C,MAAMiB,SAAS,GAAG,IAAAP,cAAO,EAAC,MAAOD,KAAK,GAAGA,KAAK,CAACQ,SAAS,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAACR,KAAK,CAAC,CAAC;EAC1E,MAAMS,QAAQ,GAAG,IAAAR,cAAO,EAAC,MAAOD,KAAK,GAAGA,KAAK,CAACS,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAACT,KAAK,CAAC,CAAC;EACxE,MAAMU,aAAa,GAAG,IAAAT,cAAO,EAC3B,MAAOO,SAAS,GAAG,CAAC,GAAG,IAAI,GAAGA,SAAS,GAAG,CAAC,CAAE,EAC7C,CAACA,SAAS,CACZ,CAAC;EACD,MAAMG,YAAY,GAAG,IAAAC,kBAAW,EAAC,MAAM;IACrC,SAAS;;IACTZ,KAAK,aAALA,KAAK,eAALA,KAAK,CAAEa,OAAO,CAAC,CAAC;EAClB,CAAC,EAAE,CAACb,KAAK,CAAC,CAAC;EAEXR,wBAAG,CAACsB,gBAAgB,CAAEC,SAAoB,IAAK;IAC7C,IAAI,CAACf,KAAK,EAAE;MACV;IACF;IACA,IAAII,QAAQ,CAACf,KAAK,IAAIiB,aAAa,CAACjB,KAAK,KAAK,CAAC,CAAC,EAAE;MAChD;IACF;IACA,MAAM;MAAE2B;IAAU,CAAC,GAAGD,SAAS;;IAE/B;IACA,IAAIR,cAAc,CAAClB,KAAK,KAAK,CAAC,CAAC,EAAE;MAC/BkB,cAAc,CAAClB,KAAK,GAAG2B,SAAS;IAClC;;IAEA;IACA,MAAMC,gBAAgB,GAAGD,SAAS,GAAGT,cAAc,CAAClB,KAAK;;IAEzD;IACA,IAAI4B,gBAAgB,GAAGR,QAAQ,IAAIvB,OAAO,CAACG,KAAK,EAAE;MAChDW,KAAK,CAACkB,IAAI,CAAC,CAAC,CAAC;MACbX,cAAc,CAAClB,KAAK,GAAG2B,SAAS;IAClC;;IAEA;IACA,MAAMG,oBAAoB,GAAGT,aAAa,GAAGzB,aAAa,CAACI,KAAK;IAChE,IACEiB,aAAa,CAACjB,KAAK,KAAK,CAAC,CAAC,IAC1B2B,SAAS,GAAGV,aAAa,CAACjB,KAAK,IAAI8B,oBAAoB,EACvD;MACA,MAAMC,GAAG,GAAGpB,KAAK,CAACqB,SAAS,CAAC,CAAC;MAC7B,IAAID,GAAG,EAAE;QACP,IAAIf,YAAY,CAAChB,KAAK,EAAE;UACtBgB,YAAY,CAAChB,KAAK,CAACwB,OAAO,CAAC,CAAC;QAC9B;QACA,IAAIS,kBAAQ,CAACC,EAAE,KAAK,SAAS,EAAE;UAC7BlB,YAAY,CAAChB,KAAK,GAAG+B,GAAG,CAACI,mBAAmB,CAAC,CAAC;QAChD,CAAC,MAAM;UACLnB,YAAY,CAAChB,KAAK,GAAG+B,GAAG;QAC1B;MACF;MACAd,aAAa,CAACjB,KAAK,GAAG2B,SAAS;IACjC;EACF,CAAC,CAAC;EAEF,IAAAS,gBAAS,EAAC,MAAM;IACd,OAAO,MAAM;MACX;MACA,IAAAC,8BAAO,EAACf,YAAY,CAAC,CAAC,CAAC;IACzB,CAAC;EACH,CAAC,EAAE,CAACA,YAAY,EAAEX,KAAK,CAAC,CAAC;EAEzB,OAAOK,YAAY;AACrB,CAAC;AAACsB,OAAA,CAAAjC,QAAA,GAAAA,QAAA"}
1
+ {"version":3,"names":["_reactNativeReanimated","require","_react","_Skia","_Platform","_ReanimatedProxy","_interopRequireDefault","obj","__esModule","default","defaultOptions","playbackSpeed","looping","paused","seek","currentTime","useOption","value","defaultValue","useSharedValue","Rea","isSharedValue","useVideo","source","userOptions","_userOptions$paused","_userOptions$looping","_userOptions$seek","_userOptions$currentT","_userOptions$playback","video","useMemo","Skia","Video","isPaused","currentFrame","lastTimestamp","startTimestamp","framerate","duration","frameDuration","disposeVideo","useCallback","dispose","useFrameCallback","frameInfo","timestamp","currentTimestamp","currentFrameDuration","Math","floor","delta","img","nextImage","Platform","OS","makeNonTextureImage","useEffect","runOnUI","exports"],"sources":["useVideo.ts"],"sourcesContent":["import {\n runOnUI,\n useSharedValue,\n type FrameInfo,\n type SharedValue,\n} from \"react-native-reanimated\";\nimport { useCallback, useEffect, useMemo } from \"react\";\n\nimport { Skia } from \"../../skia/Skia\";\nimport type { SkImage } from \"../../skia/types\";\nimport { Platform } from \"../../Platform\";\n\nimport Rea from \"./ReanimatedProxy\";\n\ntype Animated<T> = SharedValue<T> | T;\n\nexport interface PlaybackOptions {\n playbackSpeed: Animated<number>;\n looping: Animated<boolean>;\n paused: Animated<boolean>;\n seek: Animated<number | null>;\n currentTime: Animated<number>;\n}\n\nconst defaultOptions = {\n playbackSpeed: 1,\n looping: true,\n paused: false,\n seek: null,\n currentTime: 0,\n};\n\nconst useOption = <T>(value: Animated<T>) => {\n \"worklet\";\n // TODO: only create defaultValue is needed (via makeMutable)\n const defaultValue = useSharedValue(\n Rea.isSharedValue(value) ? value.value : value\n );\n return Rea.isSharedValue(value) ? value : defaultValue;\n};\n\nexport const useVideo = (\n source: string | null,\n userOptions?: Partial<PlaybackOptions>\n) => {\n const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);\n const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);\n const looping = useOption(userOptions?.looping ?? defaultOptions.looping);\n const seek = useOption(userOptions?.seek ?? defaultOptions.seek);\n const currentTime = useOption(\n userOptions?.currentTime ?? defaultOptions.currentTime\n );\n const playbackSpeed = useOption(\n userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed\n );\n const currentFrame = Rea.useSharedValue<null | SkImage>(null);\n const lastTimestamp = Rea.useSharedValue(-1);\n const startTimestamp = Rea.useSharedValue(-1);\n\n const framerate = useMemo(() => (video ? video.framerate() : -1), [video]);\n const duration = useMemo(() => (video ? video.duration() : -1), [video]);\n const frameDuration = useMemo(\n () => (framerate > 0 ? 1000 / framerate : -1),\n [framerate]\n );\n const disposeVideo = useCallback(() => {\n \"worklet\";\n video?.dispose();\n }, [video]);\n\n Rea.useFrameCallback((frameInfo: FrameInfo) => {\n if (!video) {\n return;\n }\n if (seek.value !== null) {\n video.seek(seek.value);\n seek.value = null;\n lastTimestamp.value = -1;\n startTimestamp.value = -1;\n }\n if (isPaused.value && lastTimestamp.value !== -1) {\n return;\n }\n const { timestamp } = frameInfo;\n\n // Initialize start timestamp\n if (startTimestamp.value === -1) {\n startTimestamp.value = timestamp;\n }\n\n // Calculate the current time in the video\n const currentTimestamp = timestamp - startTimestamp.value;\n currentTime.value = currentTimestamp;\n\n // Handle looping\n if (currentTimestamp > duration && looping.value) {\n video.seek(0);\n startTimestamp.value = timestamp;\n }\n\n // Update frame only if the elapsed time since last update is greater than the frame duration\n const currentFrameDuration = Math.floor(\n frameDuration / playbackSpeed.value\n );\n const delta = Math.floor(timestamp - lastTimestamp.value);\n if (lastTimestamp.value === -1 || delta >= currentFrameDuration) {\n const img = video.nextImage();\n if (img) {\n if (currentFrame.value) {\n currentFrame.value.dispose();\n }\n if (Platform.OS === \"android\") {\n currentFrame.value = img.makeNonTextureImage();\n } else {\n currentFrame.value = img;\n }\n }\n lastTimestamp.value = timestamp;\n }\n });\n\n useEffect(() => {\n return () => {\n // TODO: should video simply be a shared value instead?\n runOnUI(disposeVideo)();\n };\n }, [disposeVideo, video]);\n\n return currentFrame;\n};\n"],"mappings":"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"}
@@ -5,6 +5,8 @@ export interface PlaybackOptions {
5
5
  playbackSpeed: Animated<number>;
6
6
  looping: Animated<boolean>;
7
7
  paused: Animated<boolean>;
8
+ seek: Animated<number | null>;
9
+ currentTime: Animated<number>;
8
10
  }
9
11
  export declare const useVideo: (source: string | null, userOptions?: Partial<PlaybackOptions>) => SharedValue<SkImage | null>;
10
12
  export {};
@@ -6,7 +6,9 @@ import Rea from "./ReanimatedProxy";
6
6
  const defaultOptions = {
7
7
  playbackSpeed: 1,
8
8
  looping: true,
9
- paused: false
9
+ paused: false,
10
+ seek: null,
11
+ currentTime: 0
10
12
  };
11
13
  const useOption = value => {
12
14
  "worklet";
@@ -16,10 +18,12 @@ const useOption = value => {
16
18
  return Rea.isSharedValue(value) ? value : defaultValue;
17
19
  };
18
20
  export const useVideo = (source, userOptions) => {
19
- var _userOptions$paused, _userOptions$looping, _userOptions$playback;
21
+ var _userOptions$paused, _userOptions$looping, _userOptions$seek, _userOptions$currentT, _userOptions$playback;
20
22
  const video = useMemo(() => source ? Skia.Video(source) : null, [source]);
21
23
  const isPaused = useOption((_userOptions$paused = userOptions === null || userOptions === void 0 ? void 0 : userOptions.paused) !== null && _userOptions$paused !== void 0 ? _userOptions$paused : defaultOptions.paused);
22
24
  const looping = useOption((_userOptions$looping = userOptions === null || userOptions === void 0 ? void 0 : userOptions.looping) !== null && _userOptions$looping !== void 0 ? _userOptions$looping : defaultOptions.looping);
25
+ const seek = useOption((_userOptions$seek = userOptions === null || userOptions === void 0 ? void 0 : userOptions.seek) !== null && _userOptions$seek !== void 0 ? _userOptions$seek : defaultOptions.seek);
26
+ const currentTime = useOption((_userOptions$currentT = userOptions === null || userOptions === void 0 ? void 0 : userOptions.currentTime) !== null && _userOptions$currentT !== void 0 ? _userOptions$currentT : defaultOptions.currentTime);
23
27
  const playbackSpeed = useOption((_userOptions$playback = userOptions === null || userOptions === void 0 ? void 0 : userOptions.playbackSpeed) !== null && _userOptions$playback !== void 0 ? _userOptions$playback : defaultOptions.playbackSpeed);
24
28
  const currentFrame = Rea.useSharedValue(null);
25
29
  const lastTimestamp = Rea.useSharedValue(-1);
@@ -36,6 +40,12 @@ export const useVideo = (source, userOptions) => {
36
40
  if (!video) {
37
41
  return;
38
42
  }
43
+ if (seek.value !== null) {
44
+ video.seek(seek.value);
45
+ seek.value = null;
46
+ lastTimestamp.value = -1;
47
+ startTimestamp.value = -1;
48
+ }
39
49
  if (isPaused.value && lastTimestamp.value !== -1) {
40
50
  return;
41
51
  }
@@ -50,6 +60,7 @@ export const useVideo = (source, userOptions) => {
50
60
 
51
61
  // Calculate the current time in the video
52
62
  const currentTimestamp = timestamp - startTimestamp.value;
63
+ currentTime.value = currentTimestamp;
53
64
 
54
65
  // Handle looping
55
66
  if (currentTimestamp > duration && looping.value) {
@@ -58,8 +69,9 @@ export const useVideo = (source, userOptions) => {
58
69
  }
59
70
 
60
71
  // Update frame only if the elapsed time since last update is greater than the frame duration
61
- const currentFrameDuration = frameDuration / playbackSpeed.value;
62
- if (lastTimestamp.value === -1 || timestamp - lastTimestamp.value >= currentFrameDuration) {
72
+ const currentFrameDuration = Math.floor(frameDuration / playbackSpeed.value);
73
+ const delta = Math.floor(timestamp - lastTimestamp.value);
74
+ if (lastTimestamp.value === -1 || delta >= currentFrameDuration) {
63
75
  const img = video.nextImage();
64
76
  if (img) {
65
77
  if (currentFrame.value) {
@@ -1 +1 @@
1
- {"version":3,"names":["runOnUI","useSharedValue","useCallback","useEffect","useMemo","Skia","Platform","Rea","defaultOptions","playbackSpeed","looping","paused","useOption","value","defaultValue","isSharedValue","useVideo","source","userOptions","_userOptions$paused","_userOptions$looping","_userOptions$playback","video","Video","isPaused","currentFrame","lastTimestamp","startTimestamp","framerate","duration","frameDuration","disposeVideo","dispose","useFrameCallback","frameInfo","timestamp","currentTimestamp","seek","currentFrameDuration","img","nextImage","OS","makeNonTextureImage"],"sources":["useVideo.ts"],"sourcesContent":["import {\n runOnUI,\n useSharedValue,\n type FrameInfo,\n type SharedValue,\n} from \"react-native-reanimated\";\nimport { useCallback, useEffect, useMemo } from \"react\";\n\nimport { Skia } from \"../../skia/Skia\";\nimport type { SkImage } from \"../../skia/types\";\nimport { Platform } from \"../../Platform\";\n\nimport Rea from \"./ReanimatedProxy\";\n\ntype Animated<T> = SharedValue<T> | T;\n\nexport interface PlaybackOptions {\n playbackSpeed: Animated<number>;\n looping: Animated<boolean>;\n paused: Animated<boolean>;\n}\n\nconst defaultOptions = {\n playbackSpeed: 1,\n looping: true,\n paused: false,\n};\n\nconst useOption = <T>(value: Animated<T>) => {\n \"worklet\";\n // TODO: only create defaultValue is needed (via makeMutable)\n const defaultValue = useSharedValue(\n Rea.isSharedValue(value) ? value.value : value\n );\n return Rea.isSharedValue(value) ? value : defaultValue;\n};\n\nexport const useVideo = (\n source: string | null,\n userOptions?: Partial<PlaybackOptions>\n) => {\n const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);\n const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);\n const looping = useOption(userOptions?.looping ?? defaultOptions.looping);\n const playbackSpeed = useOption(\n userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed\n );\n const currentFrame = Rea.useSharedValue<null | SkImage>(null);\n const lastTimestamp = Rea.useSharedValue(-1);\n const startTimestamp = Rea.useSharedValue(-1);\n\n const framerate = useMemo(() => (video ? video.framerate() : -1), [video]);\n const duration = useMemo(() => (video ? video.duration() : -1), [video]);\n const frameDuration = useMemo(\n () => (framerate > 0 ? 1000 / framerate : -1),\n [framerate]\n );\n const disposeVideo = useCallback(() => {\n \"worklet\";\n video?.dispose();\n }, [video]);\n\n Rea.useFrameCallback((frameInfo: FrameInfo) => {\n if (!video) {\n return;\n }\n if (isPaused.value && lastTimestamp.value !== -1) {\n return;\n }\n const { timestamp } = frameInfo;\n\n // Initialize start timestamp\n if (startTimestamp.value === -1) {\n startTimestamp.value = timestamp;\n }\n\n // Calculate the current time in the video\n const currentTimestamp = timestamp - startTimestamp.value;\n\n // Handle looping\n if (currentTimestamp > duration && looping.value) {\n video.seek(0);\n startTimestamp.value = timestamp;\n }\n\n // Update frame only if the elapsed time since last update is greater than the frame duration\n const currentFrameDuration = frameDuration / playbackSpeed.value;\n if (\n lastTimestamp.value === -1 ||\n timestamp - lastTimestamp.value >= currentFrameDuration\n ) {\n const img = video.nextImage();\n if (img) {\n if (currentFrame.value) {\n currentFrame.value.dispose();\n }\n if (Platform.OS === \"android\") {\n currentFrame.value = img.makeNonTextureImage();\n } else {\n currentFrame.value = img;\n }\n }\n lastTimestamp.value = timestamp;\n }\n });\n\n useEffect(() => {\n return () => {\n // TODO: should video simply be a shared value instead?\n runOnUI(disposeVideo)();\n };\n }, [disposeVideo, video]);\n\n return currentFrame;\n};\n"],"mappings":"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"}
1
+ {"version":3,"names":["runOnUI","useSharedValue","useCallback","useEffect","useMemo","Skia","Platform","Rea","defaultOptions","playbackSpeed","looping","paused","seek","currentTime","useOption","value","defaultValue","isSharedValue","useVideo","source","userOptions","_userOptions$paused","_userOptions$looping","_userOptions$seek","_userOptions$currentT","_userOptions$playback","video","Video","isPaused","currentFrame","lastTimestamp","startTimestamp","framerate","duration","frameDuration","disposeVideo","dispose","useFrameCallback","frameInfo","timestamp","currentTimestamp","currentFrameDuration","Math","floor","delta","img","nextImage","OS","makeNonTextureImage"],"sources":["useVideo.ts"],"sourcesContent":["import {\n runOnUI,\n useSharedValue,\n type FrameInfo,\n type SharedValue,\n} from \"react-native-reanimated\";\nimport { useCallback, useEffect, useMemo } from \"react\";\n\nimport { Skia } from \"../../skia/Skia\";\nimport type { SkImage } from \"../../skia/types\";\nimport { Platform } from \"../../Platform\";\n\nimport Rea from \"./ReanimatedProxy\";\n\ntype Animated<T> = SharedValue<T> | T;\n\nexport interface PlaybackOptions {\n playbackSpeed: Animated<number>;\n looping: Animated<boolean>;\n paused: Animated<boolean>;\n seek: Animated<number | null>;\n currentTime: Animated<number>;\n}\n\nconst defaultOptions = {\n playbackSpeed: 1,\n looping: true,\n paused: false,\n seek: null,\n currentTime: 0,\n};\n\nconst useOption = <T>(value: Animated<T>) => {\n \"worklet\";\n // TODO: only create defaultValue is needed (via makeMutable)\n const defaultValue = useSharedValue(\n Rea.isSharedValue(value) ? value.value : value\n );\n return Rea.isSharedValue(value) ? value : defaultValue;\n};\n\nexport const useVideo = (\n source: string | null,\n userOptions?: Partial<PlaybackOptions>\n) => {\n const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);\n const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);\n const looping = useOption(userOptions?.looping ?? defaultOptions.looping);\n const seek = useOption(userOptions?.seek ?? defaultOptions.seek);\n const currentTime = useOption(\n userOptions?.currentTime ?? defaultOptions.currentTime\n );\n const playbackSpeed = useOption(\n userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed\n );\n const currentFrame = Rea.useSharedValue<null | SkImage>(null);\n const lastTimestamp = Rea.useSharedValue(-1);\n const startTimestamp = Rea.useSharedValue(-1);\n\n const framerate = useMemo(() => (video ? video.framerate() : -1), [video]);\n const duration = useMemo(() => (video ? video.duration() : -1), [video]);\n const frameDuration = useMemo(\n () => (framerate > 0 ? 1000 / framerate : -1),\n [framerate]\n );\n const disposeVideo = useCallback(() => {\n \"worklet\";\n video?.dispose();\n }, [video]);\n\n Rea.useFrameCallback((frameInfo: FrameInfo) => {\n if (!video) {\n return;\n }\n if (seek.value !== null) {\n video.seek(seek.value);\n seek.value = null;\n lastTimestamp.value = -1;\n startTimestamp.value = -1;\n }\n if (isPaused.value && lastTimestamp.value !== -1) {\n return;\n }\n const { timestamp } = frameInfo;\n\n // Initialize start timestamp\n if (startTimestamp.value === -1) {\n startTimestamp.value = timestamp;\n }\n\n // Calculate the current time in the video\n const currentTimestamp = timestamp - startTimestamp.value;\n currentTime.value = currentTimestamp;\n\n // Handle looping\n if (currentTimestamp > duration && looping.value) {\n video.seek(0);\n startTimestamp.value = timestamp;\n }\n\n // Update frame only if the elapsed time since last update is greater than the frame duration\n const currentFrameDuration = Math.floor(\n frameDuration / playbackSpeed.value\n );\n const delta = Math.floor(timestamp - lastTimestamp.value);\n if (lastTimestamp.value === -1 || delta >= currentFrameDuration) {\n const img = video.nextImage();\n if (img) {\n if (currentFrame.value) {\n currentFrame.value.dispose();\n }\n if (Platform.OS === \"android\") {\n currentFrame.value = img.makeNonTextureImage();\n } else {\n currentFrame.value = img;\n }\n }\n lastTimestamp.value = timestamp;\n }\n });\n\n useEffect(() => {\n return () => {\n // TODO: should video simply be a shared value instead?\n runOnUI(disposeVideo)();\n };\n }, [disposeVideo, video]);\n\n return currentFrame;\n};\n"],"mappings":"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"}
@@ -5,6 +5,8 @@ export interface PlaybackOptions {
5
5
  playbackSpeed: Animated<number>;
6
6
  looping: Animated<boolean>;
7
7
  paused: Animated<boolean>;
8
+ seek: Animated<number | null>;
9
+ currentTime: Animated<number>;
8
10
  }
9
11
  export declare const useVideo: (source: string | null, userOptions?: Partial<PlaybackOptions>) => SharedValue<SkImage | null>;
10
12
  export {};
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "setup-skia-web": "./scripts/setup-canvaskit.js"
8
8
  },
9
9
  "title": "React Native Skia",
10
- "version": "1.3.0",
10
+ "version": "1.3.1",
11
11
  "description": "High-performance React Native Graphics using Skia",
12
12
  "main": "lib/module/index.js",
13
13
  "react-native": "src/index.ts",
@@ -18,12 +18,16 @@ export interface PlaybackOptions {
18
18
  playbackSpeed: Animated<number>;
19
19
  looping: Animated<boolean>;
20
20
  paused: Animated<boolean>;
21
+ seek: Animated<number | null>;
22
+ currentTime: Animated<number>;
21
23
  }
22
24
 
23
25
  const defaultOptions = {
24
26
  playbackSpeed: 1,
25
27
  looping: true,
26
28
  paused: false,
29
+ seek: null,
30
+ currentTime: 0,
27
31
  };
28
32
 
29
33
  const useOption = <T>(value: Animated<T>) => {
@@ -42,6 +46,10 @@ export const useVideo = (
42
46
  const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);
43
47
  const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);
44
48
  const looping = useOption(userOptions?.looping ?? defaultOptions.looping);
49
+ const seek = useOption(userOptions?.seek ?? defaultOptions.seek);
50
+ const currentTime = useOption(
51
+ userOptions?.currentTime ?? defaultOptions.currentTime
52
+ );
45
53
  const playbackSpeed = useOption(
46
54
  userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed
47
55
  );
@@ -64,6 +72,12 @@ export const useVideo = (
64
72
  if (!video) {
65
73
  return;
66
74
  }
75
+ if (seek.value !== null) {
76
+ video.seek(seek.value);
77
+ seek.value = null;
78
+ lastTimestamp.value = -1;
79
+ startTimestamp.value = -1;
80
+ }
67
81
  if (isPaused.value && lastTimestamp.value !== -1) {
68
82
  return;
69
83
  }
@@ -76,6 +90,7 @@ export const useVideo = (
76
90
 
77
91
  // Calculate the current time in the video
78
92
  const currentTimestamp = timestamp - startTimestamp.value;
93
+ currentTime.value = currentTimestamp;
79
94
 
80
95
  // Handle looping
81
96
  if (currentTimestamp > duration && looping.value) {
@@ -84,11 +99,11 @@ export const useVideo = (
84
99
  }
85
100
 
86
101
  // 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
- ) {
102
+ const currentFrameDuration = Math.floor(
103
+ frameDuration / playbackSpeed.value
104
+ );
105
+ const delta = Math.floor(timestamp - lastTimestamp.value);
106
+ if (lastTimestamp.value === -1 || delta >= currentFrameDuration) {
92
107
  const img = video.nextImage();
93
108
  if (img) {
94
109
  if (currentFrame.value) {