@shopify/react-native-skia 1.3.2 → 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. package/android/cpp/jni/JniPlatformContext.cpp +4 -3
  2. package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +59 -1
  3. package/android/cpp/rnskia-android/RNSkAndroidVideo.h +4 -0
  4. package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +80 -7
  5. package/cpp/api/JsiVideo.h +32 -7
  6. package/cpp/rnskia/RNSkVideo.h +4 -0
  7. package/ios/RNSkia-iOS/RNSkiOSVideo.h +13 -4
  8. package/ios/RNSkia-iOS/RNSkiOSVideo.mm +65 -67
  9. package/lib/commonjs/dom/nodes/datatypes/Fitting.js +42 -30
  10. package/lib/commonjs/dom/nodes/datatypes/Fitting.js.map +1 -1
  11. package/lib/commonjs/external/reanimated/useVideo.d.ts +16 -4
  12. package/lib/commonjs/external/reanimated/useVideo.js +92 -17
  13. package/lib/commonjs/external/reanimated/useVideo.js.map +1 -1
  14. package/lib/commonjs/external/reanimated/useVideoLoading.d.ts +4 -0
  15. package/lib/commonjs/external/reanimated/useVideoLoading.js +27 -0
  16. package/lib/commonjs/external/reanimated/useVideoLoading.js.map +1 -0
  17. package/lib/commonjs/external/reanimated/useVideoLoading.web.d.ts +4 -0
  18. package/lib/commonjs/external/reanimated/useVideoLoading.web.js +20 -0
  19. package/lib/commonjs/external/reanimated/useVideoLoading.web.js.map +1 -0
  20. package/lib/commonjs/renderer/components/shapes/FitBox.d.ts +2 -10
  21. package/lib/commonjs/renderer/components/shapes/FitBox.js +32 -3
  22. package/lib/commonjs/renderer/components/shapes/FitBox.js.map +1 -1
  23. package/lib/commonjs/skia/core/Matrix.js +5 -1
  24. package/lib/commonjs/skia/core/Matrix.js.map +1 -1
  25. package/lib/commonjs/skia/types/Matrix.js +2 -0
  26. package/lib/commonjs/skia/types/Matrix.js.map +1 -1
  27. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +3 -1
  28. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js +4 -2
  29. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
  30. package/lib/commonjs/skia/types/Skia.d.ts +1 -1
  31. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  32. package/lib/commonjs/skia/types/Video/Video.d.ts +9 -1
  33. package/lib/commonjs/skia/types/Video/Video.js.map +1 -1
  34. package/lib/commonjs/skia/web/CanvasKitWebGLBufferImpl.d.ts +9 -0
  35. package/lib/commonjs/skia/web/CanvasKitWebGLBufferImpl.js +30 -0
  36. package/lib/commonjs/skia/web/CanvasKitWebGLBufferImpl.js.map +1 -0
  37. package/lib/commonjs/skia/web/JsiSkImageFactory.js +8 -2
  38. package/lib/commonjs/skia/web/JsiSkImageFactory.js.map +1 -1
  39. package/lib/commonjs/skia/web/JsiSkia.js +2 -3
  40. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  41. package/lib/commonjs/skia/web/JsiVideo.d.ts +24 -0
  42. package/lib/commonjs/skia/web/JsiVideo.js +83 -0
  43. package/lib/commonjs/skia/web/JsiVideo.js.map +1 -0
  44. package/lib/commonjs/views/SkiaDomView.js +2 -0
  45. package/lib/commonjs/views/SkiaDomView.js.map +1 -1
  46. package/lib/commonjs/views/SkiaDomView.web.js +2 -0
  47. package/lib/commonjs/views/SkiaDomView.web.js.map +1 -1
  48. package/lib/commonjs/views/SkiaJSDomView.js +2 -0
  49. package/lib/commonjs/views/SkiaJSDomView.js.map +1 -1
  50. package/lib/module/dom/nodes/datatypes/Fitting.js +41 -29
  51. package/lib/module/dom/nodes/datatypes/Fitting.js.map +1 -1
  52. package/lib/module/external/reanimated/useVideo.d.ts +16 -4
  53. package/lib/module/external/reanimated/useVideo.js +92 -17
  54. package/lib/module/external/reanimated/useVideo.js.map +1 -1
  55. package/lib/module/external/reanimated/useVideoLoading.d.ts +4 -0
  56. package/lib/module/external/reanimated/useVideoLoading.js +20 -0
  57. package/lib/module/external/reanimated/useVideoLoading.js.map +1 -0
  58. package/lib/module/external/reanimated/useVideoLoading.web.d.ts +4 -0
  59. package/lib/module/external/reanimated/useVideoLoading.web.js +13 -0
  60. package/lib/module/external/reanimated/useVideoLoading.web.js.map +1 -0
  61. package/lib/module/renderer/components/shapes/FitBox.d.ts +2 -10
  62. package/lib/module/renderer/components/shapes/FitBox.js +32 -3
  63. package/lib/module/renderer/components/shapes/FitBox.js.map +1 -1
  64. package/lib/module/skia/core/Matrix.js +5 -1
  65. package/lib/module/skia/core/Matrix.js.map +1 -1
  66. package/lib/module/skia/types/Matrix.js +2 -0
  67. package/lib/module/skia/types/Matrix.js.map +1 -1
  68. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +3 -1
  69. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js +2 -1
  70. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
  71. package/lib/module/skia/types/Skia.d.ts +1 -1
  72. package/lib/module/skia/types/Skia.js.map +1 -1
  73. package/lib/module/skia/types/Video/Video.d.ts +9 -1
  74. package/lib/module/skia/types/Video/Video.js.map +1 -1
  75. package/lib/module/skia/web/CanvasKitWebGLBufferImpl.d.ts +9 -0
  76. package/lib/module/skia/web/CanvasKitWebGLBufferImpl.js +23 -0
  77. package/lib/module/skia/web/CanvasKitWebGLBufferImpl.js.map +1 -0
  78. package/lib/module/skia/web/JsiSkImageFactory.js +9 -3
  79. package/lib/module/skia/web/JsiSkImageFactory.js.map +1 -1
  80. package/lib/module/skia/web/JsiSkia.js +2 -3
  81. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  82. package/lib/module/skia/web/JsiVideo.d.ts +24 -0
  83. package/lib/module/skia/web/JsiVideo.js +75 -0
  84. package/lib/module/skia/web/JsiVideo.js.map +1 -0
  85. package/lib/module/views/SkiaDomView.js +2 -0
  86. package/lib/module/views/SkiaDomView.js.map +1 -1
  87. package/lib/module/views/SkiaDomView.web.js +2 -0
  88. package/lib/module/views/SkiaDomView.web.js.map +1 -1
  89. package/lib/module/views/SkiaJSDomView.js +2 -0
  90. package/lib/module/views/SkiaJSDomView.js.map +1 -1
  91. package/lib/typescript/src/external/reanimated/useVideo.d.ts +16 -4
  92. package/lib/typescript/src/external/reanimated/useVideoLoading.d.ts +4 -0
  93. package/lib/typescript/src/external/reanimated/useVideoLoading.web.d.ts +4 -0
  94. package/lib/typescript/src/renderer/components/shapes/FitBox.d.ts +2 -10
  95. package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +3 -1
  96. package/lib/typescript/src/skia/types/Skia.d.ts +1 -1
  97. package/lib/typescript/src/skia/types/Video/Video.d.ts +9 -1
  98. package/lib/typescript/src/skia/web/CanvasKitWebGLBufferImpl.d.ts +9 -0
  99. package/lib/typescript/src/skia/web/JsiVideo.d.ts +24 -0
  100. package/package.json +1 -1
  101. package/src/dom/nodes/datatypes/Fitting.ts +28 -21
  102. package/src/external/reanimated/useVideo.ts +108 -31
  103. package/src/external/reanimated/useVideoLoading.ts +28 -0
  104. package/src/external/reanimated/useVideoLoading.web.ts +17 -0
  105. package/src/renderer/components/shapes/FitBox.tsx +38 -4
  106. package/src/skia/core/Matrix.ts +4 -2
  107. package/src/skia/types/Matrix.ts +1 -0
  108. package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +10 -2
  109. package/src/skia/types/Skia.ts +1 -1
  110. package/src/skia/types/Video/Video.ts +7 -1
  111. package/src/skia/web/CanvasKitWebGLBufferImpl.ts +22 -0
  112. package/src/skia/web/JsiSkImageFactory.ts +16 -3
  113. package/src/skia/web/JsiSkia.ts +2 -3
  114. package/src/skia/web/JsiVideo.ts +96 -0
  115. package/src/views/SkiaDomView.tsx +4 -0
  116. package/src/views/SkiaDomView.web.tsx +4 -0
  117. package/src/views/SkiaJSDomView.tsx +4 -0
  118. package/lib/commonjs/external/reanimated/video.d.ts +0 -16
  119. package/lib/commonjs/external/reanimated/video.js +0 -54
  120. package/lib/commonjs/external/reanimated/video.js.map +0 -1
  121. package/lib/module/external/reanimated/video.d.ts +0 -16
  122. package/lib/module/external/reanimated/video.js +0 -46
  123. package/lib/module/external/reanimated/video.js.map +0 -1
  124. package/lib/typescript/src/external/reanimated/video.d.ts +0 -16
  125. package/src/external/reanimated/video.ts +0 -82
@@ -77,6 +77,8 @@ TSelf JniPlatformContext::initHybrid(jni::alias_ref<jhybridobject> jThis,
77
77
 
78
78
  jni::global_ref<jobject>
79
79
  JniPlatformContext::createVideo(const std::string &url) {
80
+ jni::Environment::ensureCurrentThreadIsAttached();
81
+
80
82
  jni::ThreadScope ts; // Manages JNI thread attachment/detachment
81
83
 
82
84
  // Get the JNI environment
@@ -85,14 +87,13 @@ JniPlatformContext::createVideo(const std::string &url) {
85
87
  // Convert std::string to jstring
86
88
  jstring jUrl = env->NewStringUTF(url.c_str());
87
89
 
88
- // Get the method ID for the createVideo method
89
- // Replace "Lcom/yourpackage/RNSkVideo;" with the actual return type
90
- // descriptor
91
90
  static auto method =
92
91
  javaPart_->getClass()->getMethod<jobject(jstring)>("createVideo");
93
92
 
94
93
  // Call the method and receive a local reference to the video object
95
94
  auto videoObject = method(javaPart_.get(), jUrl);
95
+ env->DeleteLocalRef(jUrl);
96
+
96
97
  // Clean up the jstring local reference
97
98
  auto result = jni::make_global(videoObject);
98
99
  return result;
@@ -9,6 +9,7 @@
9
9
  #pragma clang diagnostic ignored "-Wdocumentation"
10
10
 
11
11
  #include "include/core/SkImage.h"
12
+ #include "include/core/SkSize.h"
12
13
 
13
14
  #pragma clang diagnostic pop
14
15
 
@@ -82,7 +83,7 @@ double RNSkAndroidVideo::framerate() {
82
83
  void RNSkAndroidVideo::seek(double timestamp) {
83
84
  JNIEnv *env = facebook::jni::Environment::current();
84
85
  jclass cls = env->GetObjectClass(_jniVideo.get());
85
- jmethodID mid = env->GetMethodID(cls, "seek", "(J)V");
86
+ jmethodID mid = env->GetMethodID(cls, "seek", "(D)V");
86
87
  if (!mid) {
87
88
  RNSkLogger::logToConsole("seek method not found");
88
89
  return;
@@ -102,4 +103,61 @@ float RNSkAndroidVideo::getRotationInDegrees() {
102
103
  return static_cast<float>(rotation);
103
104
  }
104
105
 
106
+ SkISize RNSkAndroidVideo::getSize() {
107
+ JNIEnv *env = facebook::jni::Environment::current();
108
+ jclass cls = env->GetObjectClass(_jniVideo.get());
109
+ jmethodID mid =
110
+ env->GetMethodID(cls, "getSize", "()Landroid/graphics/Point;");
111
+ if (!mid) {
112
+ RNSkLogger::logToConsole("getSize method not found");
113
+ return SkISize::Make(0, 0);
114
+ }
115
+ jobject jPoint = env->CallObjectMethod(_jniVideo.get(), mid);
116
+ jclass pointCls = env->GetObjectClass(jPoint);
117
+
118
+ jfieldID xFid = env->GetFieldID(pointCls, "x", "I");
119
+ jfieldID yFid = env->GetFieldID(pointCls, "y", "I");
120
+ if (!xFid || !yFid) {
121
+ RNSkLogger::logToConsole("Point class fields not found");
122
+ return SkISize::Make(0, 0);
123
+ }
124
+
125
+ jint width = env->GetIntField(jPoint, xFid);
126
+ jint height = env->GetIntField(jPoint, yFid);
127
+
128
+ return SkISize::Make(width, height);
129
+ }
130
+
131
+ void RNSkAndroidVideo::play() {
132
+ JNIEnv *env = facebook::jni::Environment::current();
133
+ jclass cls = env->GetObjectClass(_jniVideo.get());
134
+ jmethodID mid = env->GetMethodID(cls, "play", "()V");
135
+ if (!mid) {
136
+ RNSkLogger::logToConsole("play method not found");
137
+ return;
138
+ }
139
+ env->CallVoidMethod(_jniVideo.get(), mid);
140
+ }
141
+
142
+ void RNSkAndroidVideo::pause() {
143
+ JNIEnv *env = facebook::jni::Environment::current();
144
+ jclass cls = env->GetObjectClass(_jniVideo.get());
145
+ jmethodID mid = env->GetMethodID(cls, "pause", "()V");
146
+ if (!mid) {
147
+ RNSkLogger::logToConsole("pause method not found");
148
+ return;
149
+ }
150
+ env->CallVoidMethod(_jniVideo.get(), mid);
151
+ }
152
+
153
+ void RNSkAndroidVideo::setVolume(float volume) {
154
+ JNIEnv *env = facebook::jni::Environment::current();
155
+ jclass cls = env->GetObjectClass(_jniVideo.get());
156
+ jmethodID mid = env->GetMethodID(cls, "setVolume", "(F)V");
157
+ if (!mid) {
158
+ RNSkLogger::logToConsole("setVolume method not found");
159
+ return;
160
+ }
161
+ env->CallVoidMethod(_jniVideo.get(), mid, volume);
162
+ }
105
163
  } // namespace RNSkia
@@ -32,6 +32,10 @@ public:
32
32
  double framerate() override;
33
33
  void seek(double timestamp) override;
34
34
  float getRotationInDegrees() override;
35
+ SkISize getSize() override;
36
+ void play() override;
37
+ void pause() override;
38
+ void setVolume(float volume) override;
35
39
  };
36
40
 
37
41
  } // namespace RNSkia
@@ -3,14 +3,19 @@ package com.shopify.reactnative.skia;
3
3
  import android.content.Context;
4
4
  import android.graphics.ImageFormat;
5
5
  import android.hardware.HardwareBuffer;
6
- import android.media.Image;
7
- import android.media.ImageReader;
6
+ import android.media.AudioAttributes;
7
+ import android.media.AudioManager;
8
8
  import android.media.MediaCodec;
9
9
  import android.media.MediaExtractor;
10
10
  import android.media.MediaFormat;
11
+ import android.media.MediaPlayer;
12
+ import android.media.MediaSync;
13
+ import android.media.Image;
14
+ import android.media.ImageReader;
11
15
  import android.net.Uri;
12
16
  import android.os.Build;
13
17
  import android.view.Surface;
18
+ import android.graphics.Point;
14
19
 
15
20
  import androidx.annotation.RequiresApi;
16
21
 
@@ -27,9 +32,15 @@ public class RNSkVideo {
27
32
  private MediaCodec decoder;
28
33
  private ImageReader imageReader;
29
34
  private Surface outputSurface;
35
+ private MediaPlayer mediaPlayer;
36
+ private MediaSync mediaSync;
30
37
  private double durationMs;
31
38
  private double frameRate;
32
39
  private int rotationDegrees = 0;
40
+ private int width = 0;
41
+ private int height = 0;
42
+
43
+ private boolean isPlaying = false;
33
44
 
34
45
  RNSkVideo(Context context, String localUri) {
35
46
  this.uri = Uri.parse(localUri);
@@ -47,6 +58,18 @@ public class RNSkVideo {
47
58
  }
48
59
  extractor.selectTrack(trackIndex);
49
60
  MediaFormat format = extractor.getTrackFormat(trackIndex);
61
+
62
+ // Initialize MediaPlayer
63
+ mediaPlayer = new MediaPlayer();
64
+ mediaPlayer.setDataSource(context, uri);
65
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
66
+ mediaPlayer.setOnPreparedListener(mp -> {
67
+ durationMs = mp.getDuration();
68
+ mp.start();
69
+ isPlaying = true;
70
+ });
71
+ mediaPlayer.prepareAsync();
72
+
50
73
  // Retrieve and store video properties
51
74
  if (format.containsKey(MediaFormat.KEY_DURATION)) {
52
75
  durationMs = format.getLong(MediaFormat.KEY_DURATION) / 1000; // Convert microseconds to milliseconds
@@ -57,8 +80,8 @@ public class RNSkVideo {
57
80
  if (format.containsKey(MediaFormat.KEY_ROTATION)) {
58
81
  rotationDegrees = format.getInteger(MediaFormat.KEY_ROTATION);
59
82
  }
60
- int width = format.getInteger(MediaFormat.KEY_WIDTH);
61
- int height = format.getInteger(MediaFormat.KEY_HEIGHT);
83
+ width = format.getInteger(MediaFormat.KEY_WIDTH);
84
+ height = format.getInteger(MediaFormat.KEY_HEIGHT);
62
85
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
63
86
  imageReader = ImageReader.newInstance(
64
87
  width,
@@ -116,15 +139,38 @@ public class RNSkVideo {
116
139
  }
117
140
 
118
141
  @DoNotStrip
119
- public void seek(long timestamp) {
120
- // Seek to the closest sync frame at or before the specified time
121
- extractor.seekTo(timestamp * 1000, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
142
+ public void seek(double timestamp) {
143
+ // Log the values for debugging
144
+
145
+ long timestampUs = (long)(timestamp * 1000); // Convert milliseconds to microseconds
146
+
147
+ extractor.seekTo(timestampUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
148
+ if (mediaPlayer != null) {
149
+ int timestampMs = (int) timestamp; // Convert to milliseconds
150
+ mediaPlayer.seekTo(timestampMs, MediaPlayer.SEEK_CLOSEST);
151
+ }
152
+
122
153
  // Flush the codec to reset internal state and buffers
123
154
  if (decoder != null) {
124
155
  decoder.flush();
156
+
157
+ // Decode frames until reaching the exact timestamp
158
+ boolean isSeeking = true;
159
+ while (isSeeking) {
160
+ decodeFrame();
161
+ long currentTimestampUs = extractor.getSampleTime();
162
+ if (currentTimestampUs >= timestampUs) {
163
+ isSeeking = false;
164
+ }
165
+ }
125
166
  }
126
167
  }
127
168
 
169
+ @DoNotStrip
170
+ public Point getSize() {
171
+ return new Point(width, height);
172
+ }
173
+
128
174
  private int selectVideoTrack(MediaExtractor extractor) {
129
175
  int numTracks = extractor.getTrackCount();
130
176
  for (int i = 0; i < numTracks; i++) {
@@ -179,7 +225,34 @@ public class RNSkVideo {
179
225
  }
180
226
  }
181
227
 
228
+ @DoNotStrip
229
+ public void play() {
230
+ if (mediaPlayer != null && !isPlaying) {
231
+ mediaPlayer.start();
232
+ isPlaying = true;
233
+ }
234
+ }
235
+
236
+ @DoNotStrip
237
+ public void pause() {
238
+ if (mediaPlayer != null && isPlaying) {
239
+ mediaPlayer.pause();
240
+ isPlaying = false;
241
+ }
242
+ }
243
+
244
+ @DoNotStrip
245
+ public void setVolume(float volume) {
246
+ if (mediaPlayer != null) {
247
+ mediaPlayer.setVolume(volume, volume);
248
+ }
249
+ }
250
+
182
251
  public void release() {
252
+ if (mediaPlayer != null) {
253
+ mediaPlayer.release();
254
+ mediaPlayer = null;
255
+ }
183
256
  if (decoder != null) {
184
257
  decoder.stop();
185
258
  decoder.release();
@@ -53,18 +53,43 @@ public:
53
53
  return jsi::Value::undefined();
54
54
  }
55
55
 
56
- JSI_HOST_FUNCTION(getRotationInDegrees) {
56
+ JSI_HOST_FUNCTION(rotation) {
57
57
  auto context = getContext();
58
58
  auto rot = getObject()->getRotationInDegrees();
59
59
  return jsi::Value(static_cast<double>(rot));
60
60
  }
61
61
 
62
- JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiVideo, nextImage),
63
- JSI_EXPORT_FUNC(JsiVideo, duration),
64
- JSI_EXPORT_FUNC(JsiVideo, framerate),
65
- JSI_EXPORT_FUNC(JsiVideo, seek),
66
- JSI_EXPORT_FUNC(JsiVideo, getRotationInDegrees),
67
- JSI_EXPORT_FUNC(JsiVideo, dispose))
62
+ JSI_HOST_FUNCTION(size) {
63
+ auto context = getContext();
64
+ auto size = getObject()->getSize();
65
+ auto result = jsi::Object(runtime);
66
+ result.setProperty(runtime, "width", static_cast<double>(size.width()));
67
+ result.setProperty(runtime, "height", static_cast<double>(size.height()));
68
+ return result;
69
+ }
70
+
71
+ JSI_HOST_FUNCTION(play) {
72
+ getObject()->play();
73
+ return jsi::Value::undefined();
74
+ }
75
+
76
+ JSI_HOST_FUNCTION(pause) {
77
+ getObject()->pause();
78
+ return jsi::Value::undefined();
79
+ }
80
+
81
+ JSI_HOST_FUNCTION(setVolume) {
82
+ auto volume = arguments[0].asNumber();
83
+ getObject()->setVolume(static_cast<float>(volume));
84
+ return jsi::Value::undefined();
85
+ }
86
+
87
+ JSI_EXPORT_FUNCTIONS(
88
+ JSI_EXPORT_FUNC(JsiVideo, nextImage), JSI_EXPORT_FUNC(JsiVideo, duration),
89
+ JSI_EXPORT_FUNC(JsiVideo, framerate), JSI_EXPORT_FUNC(JsiVideo, seek),
90
+ JSI_EXPORT_FUNC(JsiVideo, rotation), JSI_EXPORT_FUNC(JsiVideo, size),
91
+ JSI_EXPORT_FUNC(JsiVideo, play), JSI_EXPORT_FUNC(JsiVideo, pause),
92
+ JSI_EXPORT_FUNC(JsiVideo, setVolume), JSI_EXPORT_FUNC(JsiVideo, dispose))
68
93
 
69
94
  JsiVideo(std::shared_ptr<RNSkPlatformContext> context,
70
95
  std::shared_ptr<RNSkVideo> video)
@@ -19,6 +19,10 @@ public:
19
19
  virtual double framerate() = 0;
20
20
  virtual void seek(double timestamp) = 0;
21
21
  virtual float getRotationInDegrees() = 0;
22
+ virtual SkISize getSize() = 0;
23
+ virtual void play() = 0;
24
+ virtual void pause() = 0;
25
+ virtual void setVolume(float volume) = 0;
22
26
  };
23
27
 
24
28
  } // namespace RNSkia
@@ -9,6 +9,7 @@
9
9
  #pragma clang diagnostic ignored "-Wdocumentation"
10
10
 
11
11
  #include "include/core/SkImage.h"
12
+ #include "include/core/SkSize.h"
12
13
 
13
14
  #pragma clang diagnostic pop
14
15
 
@@ -20,14 +21,18 @@ namespace RNSkia {
20
21
  class RNSkiOSVideo : public RNSkVideo {
21
22
  private:
22
23
  std::string _url;
23
- AVAssetReader *_reader = nullptr;
24
- AVAssetReaderTrackOutput *_trackOutput = nullptr;
24
+ AVPlayer *_player = nullptr;
25
+ AVPlayerItem *_playerItem = nullptr;
26
+ AVPlayerItemVideoOutput *_videoOutput = nullptr;
25
27
  RNSkPlatformContext *_context;
26
28
  double _duration = 0;
27
29
  double _framerate = 0;
28
- void setupReader(CMTimeRange timeRange);
29
- NSDictionary *getOutputSettings();
30
+ float _videoWidth = 0;
31
+ float _videoHeight = 0;
30
32
  CGAffineTransform _preferredTransform;
33
+ bool _isPlaying = false;
34
+ void setupPlayer();
35
+ NSDictionary *getOutputSettings();
31
36
 
32
37
  public:
33
38
  RNSkiOSVideo(std::string url, RNSkPlatformContext *context);
@@ -36,7 +41,11 @@ public:
36
41
  double duration() override;
37
42
  double framerate() override;
38
43
  void seek(double timestamp) override;
44
+ void play();
45
+ void pause();
39
46
  float getRotationInDegrees() override;
47
+ SkISize getSize() override;
48
+ void setVolume(float volume);
40
49
  };
41
50
 
42
51
  } // namespace RNSkia
@@ -16,78 +16,60 @@ namespace RNSkia {
16
16
 
17
17
  RNSkiOSVideo::RNSkiOSVideo(std::string url, RNSkPlatformContext *context)
18
18
  : _url(std::move(url)), _context(context) {
19
- setupReader(CMTimeRangeMake(kCMTimeZero, kCMTimePositiveInfinity));
19
+ setupPlayer();
20
20
  }
21
21
 
22
- RNSkiOSVideo::~RNSkiOSVideo() {}
22
+ RNSkiOSVideo::~RNSkiOSVideo() {
23
+ if (_player) {
24
+ [_player pause];
25
+ }
26
+ }
23
27
 
24
- void RNSkiOSVideo::setupReader(CMTimeRange timeRange) {
25
- NSError *error = nil;
28
+ void RNSkiOSVideo::setupPlayer() {
29
+ NSURL *videoURL = [NSURL URLWithString:@(_url.c_str())];
30
+ AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:videoURL];
31
+ _player = [AVPlayer playerWithPlayerItem:playerItem];
32
+ _playerItem = playerItem;
26
33
 
27
- AVURLAsset *asset =
28
- [AVURLAsset URLAssetWithURL:[NSURL URLWithString:@(_url.c_str())]
29
- options:nil];
30
- AVAssetReader *assetReader = [[AVAssetReader alloc] initWithAsset:asset
31
- error:&error];
32
- if (error) {
33
- NSLog(@"Error initializing asset reader: %@", error.localizedDescription);
34
- return;
35
- }
34
+ NSDictionary *outputSettings = getOutputSettings();
35
+ _videoOutput =
36
+ [[AVPlayerItemVideoOutput alloc] initWithOutputSettings:outputSettings];
37
+ [playerItem addOutput:_videoOutput];
36
38
 
37
- CMTime time = [asset duration];
38
- if (time.timescale == 0) {
39
- NSLog(@"Error: Timescale of the asset is zero.");
40
- return;
39
+ CMTime time = playerItem.asset.duration;
40
+ if (time.timescale != 0) {
41
+ _duration = CMTimeGetSeconds(time) * 1000; // Store duration in milliseconds
41
42
  }
42
43
 
43
- _duration = CMTimeGetSeconds(time) * 1000; // Store duration in milliseconds
44
44
  AVAssetTrack *videoTrack =
45
- [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
46
- _framerate = videoTrack.nominalFrameRate;
47
- _preferredTransform = videoTrack.preferredTransform;
48
-
49
- NSDictionary *outputSettings = getOutputSettings();
50
- AVAssetReaderTrackOutput *trackOutput =
51
- [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack
52
- outputSettings:outputSettings];
53
-
54
- assetReader.timeRange = timeRange;
55
- if ([assetReader canAddOutput:trackOutput]) {
56
- [assetReader addOutput:trackOutput];
57
- [assetReader startReading];
58
- } else {
59
- NSLog(@"Cannot add output to asset reader.");
60
- return;
45
+ [[playerItem.asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
46
+ if (videoTrack) {
47
+ _framerate = videoTrack.nominalFrameRate;
48
+ _preferredTransform = videoTrack.preferredTransform;
49
+ CGSize videoSize = videoTrack.naturalSize;
50
+ _videoWidth = videoSize.width;
51
+ _videoHeight = videoSize.height;
61
52
  }
62
-
63
- _reader = assetReader;
64
- _trackOutput = trackOutput;
53
+ play();
65
54
  }
66
55
 
67
56
  sk_sp<SkImage> RNSkiOSVideo::nextImage(double *timeStamp) {
68
- CMSampleBufferRef sampleBuffer = [_trackOutput copyNextSampleBuffer];
69
- if (!sampleBuffer) {
70
- NSLog(@"No sample buffer.");
71
- return nullptr;
72
- }
73
-
74
- // Extract the pixel buffer from the sample buffer
75
- CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
57
+ CMTime currentTime = [_player currentTime];
58
+ CVPixelBufferRef pixelBuffer =
59
+ [_videoOutput copyPixelBufferForItemTime:currentTime
60
+ itemTimeForDisplay:nullptr];
76
61
  if (!pixelBuffer) {
77
62
  NSLog(@"No pixel buffer.");
78
- CFRelease(sampleBuffer);
79
63
  return nullptr;
80
64
  }
81
65
 
82
- auto skImage = _context->makeImageFromNativeBuffer(
83
- reinterpret_cast<void *>(pixelBuffer));
66
+ auto skImage = _context->makeImageFromNativeBuffer((void *)pixelBuffer);
84
67
 
85
68
  if (timeStamp) {
86
- CMTime time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
87
- *timeStamp = CMTimeGetSeconds(time);
69
+ *timeStamp = CMTimeGetSeconds(currentTime);
88
70
  }
89
71
 
90
- CFRelease(sampleBuffer);
72
+ CVPixelBufferRelease(pixelBuffer);
91
73
  return skImage;
92
74
  }
93
75
 
@@ -104,36 +86,52 @@ float RNSkiOSVideo::getRotationInDegrees() {
104
86
  // Determine the rotation angle in radians
105
87
  if (transform.a == 0 && transform.b == 1 && transform.c == -1 &&
106
88
  transform.d == 0) {
107
- rotationAngle = M_PI_2; // 90 degrees
89
+ rotationAngle = 90;
108
90
  } else if (transform.a == 0 && transform.b == -1 && transform.c == 1 &&
109
91
  transform.d == 0) {
110
- rotationAngle = -M_PI_2; // -90 degrees
92
+ rotationAngle = 270;
111
93
  } else if (transform.a == -1 && transform.b == 0 && transform.c == 0 &&
112
94
  transform.d == -1) {
113
- rotationAngle = M_PI; // 180 degrees
114
- } else if (transform.a == 1 && transform.b == 0 && transform.c == 0 &&
115
- transform.d == 1) {
116
- rotationAngle = 0.0; // 0 degrees
95
+ rotationAngle = 180;
117
96
  }
118
- // Convert the rotation angle from radians to degrees
119
- return rotationAngle * 180 / M_PI;
97
+ return rotationAngle;
120
98
  }
121
99
 
122
100
  void RNSkiOSVideo::seek(double timeInMilliseconds) {
123
- if (_reader) {
124
- [_reader cancelReading];
125
- _reader = nil;
126
- _trackOutput = nil;
101
+ CMTime seekTime =
102
+ CMTimeMakeWithSeconds(timeInMilliseconds / 1000.0, NSEC_PER_SEC);
103
+ [_player seekToTime:seekTime
104
+ toleranceBefore:kCMTimeZero
105
+ toleranceAfter:kCMTimeZero
106
+ completionHandler:^(BOOL finished) {
107
+ if (!finished) {
108
+ NSLog(@"Seek failed or was interrupted.");
109
+ }
110
+ }];
111
+ }
112
+
113
+ void RNSkiOSVideo::play() {
114
+ if (_player) {
115
+ [_player play];
116
+ _isPlaying = true;
127
117
  }
118
+ }
128
119
 
129
- CMTime startTime =
130
- CMTimeMakeWithSeconds(timeInMilliseconds / 1000.0, NSEC_PER_SEC);
131
- CMTimeRange timeRange = CMTimeRangeMake(startTime, kCMTimePositiveInfinity);
132
- setupReader(timeRange);
120
+ void RNSkiOSVideo::pause() {
121
+ if (_player) {
122
+ [_player pause];
123
+ _isPlaying = false;
124
+ }
133
125
  }
134
126
 
135
127
  double RNSkiOSVideo::duration() { return _duration; }
136
128
 
137
129
  double RNSkiOSVideo::framerate() { return _framerate; }
138
130
 
131
+ SkISize RNSkiOSVideo::getSize() {
132
+ return SkISize::Make(_videoWidth, _videoHeight);
133
+ }
134
+
135
+ void RNSkiOSVideo::setVolume(float volume) { _player.volume = volume; }
136
+
139
137
  } // namespace RNSkia
@@ -5,12 +5,18 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.size = exports.rect2rect = exports.fitRects = void 0;
7
7
  var _typeddash = require("../../../renderer/typeddash");
8
- const size = (width = 0, height = 0) => ({
9
- width,
10
- height
11
- });
8
+ const size = (width = 0, height = 0) => {
9
+ "worklet";
10
+
11
+ return {
12
+ width,
13
+ height
14
+ };
15
+ };
12
16
  exports.size = size;
13
17
  const rect2rect = (src, dst) => {
18
+ "worklet";
19
+
14
20
  const scaleX = dst.width / src.width;
15
21
  const scaleY = dst.height / src.height;
16
22
  const translateX = dst.x - src.x * scaleX;
@@ -26,36 +32,12 @@ const rect2rect = (src, dst) => {
26
32
  }];
27
33
  };
28
34
  exports.rect2rect = rect2rect;
29
- const fitRects = (fit, rect, {
30
- x,
31
- y,
32
- width,
33
- height
34
- }) => {
35
- const sizes = applyBoxFit(fit, {
36
- width: rect.width,
37
- height: rect.height
38
- }, {
39
- width,
40
- height
41
- });
42
- const src = inscribe(sizes.src, rect);
43
- const dst = inscribe(sizes.dst, {
44
- x,
45
- y,
46
- width,
47
- height
48
- });
49
- return {
50
- src,
51
- dst
52
- };
53
- };
54
- exports.fitRects = fitRects;
55
35
  const inscribe = ({
56
36
  width,
57
37
  height
58
38
  }, rect) => {
39
+ "worklet";
40
+
59
41
  const halfWidthDelta = (rect.width - width) / 2.0;
60
42
  const halfHeightDelta = (rect.height - height) / 2.0;
61
43
  return {
@@ -66,6 +48,8 @@ const inscribe = ({
66
48
  };
67
49
  };
68
50
  const applyBoxFit = (fit, input, output) => {
51
+ "worklet";
52
+
69
53
  let src = size(),
70
54
  dst = size();
71
55
  if (input.height <= 0.0 || input.width <= 0.0 || output.height <= 0.0 || output.width <= 0.0) {
@@ -126,4 +110,32 @@ const applyBoxFit = (fit, input, output) => {
126
110
  dst
127
111
  };
128
112
  };
113
+ const fitRects = (fit, rect, {
114
+ x,
115
+ y,
116
+ width,
117
+ height
118
+ }) => {
119
+ "worklet";
120
+
121
+ const sizes = applyBoxFit(fit, {
122
+ width: rect.width,
123
+ height: rect.height
124
+ }, {
125
+ width,
126
+ height
127
+ });
128
+ const src = inscribe(sizes.src, rect);
129
+ const dst = inscribe(sizes.dst, {
130
+ x,
131
+ y,
132
+ width,
133
+ height
134
+ });
135
+ return {
136
+ src,
137
+ dst
138
+ };
139
+ };
140
+ exports.fitRects = fitRects;
129
141
  //# sourceMappingURL=Fitting.js.map