@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.
- package/android/cpp/jni/JniPlatformContext.cpp +4 -3
- package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +59 -1
- package/android/cpp/rnskia-android/RNSkAndroidVideo.h +4 -0
- package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +80 -7
- package/cpp/api/JsiVideo.h +32 -7
- package/cpp/rnskia/RNSkVideo.h +4 -0
- package/ios/RNSkia-iOS/RNSkiOSVideo.h +13 -4
- package/ios/RNSkia-iOS/RNSkiOSVideo.mm +65 -67
- package/lib/commonjs/dom/nodes/datatypes/Fitting.js +42 -30
- package/lib/commonjs/dom/nodes/datatypes/Fitting.js.map +1 -1
- package/lib/commonjs/external/reanimated/useVideo.d.ts +16 -4
- package/lib/commonjs/external/reanimated/useVideo.js +92 -17
- package/lib/commonjs/external/reanimated/useVideo.js.map +1 -1
- package/lib/commonjs/external/reanimated/useVideoLoading.d.ts +4 -0
- package/lib/commonjs/external/reanimated/useVideoLoading.js +27 -0
- package/lib/commonjs/external/reanimated/useVideoLoading.js.map +1 -0
- package/lib/commonjs/external/reanimated/useVideoLoading.web.d.ts +4 -0
- package/lib/commonjs/external/reanimated/useVideoLoading.web.js +20 -0
- package/lib/commonjs/external/reanimated/useVideoLoading.web.js.map +1 -0
- package/lib/commonjs/renderer/components/shapes/FitBox.d.ts +2 -10
- package/lib/commonjs/renderer/components/shapes/FitBox.js +32 -3
- package/lib/commonjs/renderer/components/shapes/FitBox.js.map +1 -1
- package/lib/commonjs/skia/core/Matrix.js +5 -1
- package/lib/commonjs/skia/core/Matrix.js.map +1 -1
- package/lib/commonjs/skia/types/Matrix.js +2 -0
- package/lib/commonjs/skia/types/Matrix.js.map +1 -1
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +3 -1
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js +4 -2
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
- package/lib/commonjs/skia/types/Skia.d.ts +1 -1
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/Video/Video.d.ts +9 -1
- package/lib/commonjs/skia/types/Video/Video.js.map +1 -1
- package/lib/commonjs/skia/web/CanvasKitWebGLBufferImpl.d.ts +9 -0
- package/lib/commonjs/skia/web/CanvasKitWebGLBufferImpl.js +30 -0
- package/lib/commonjs/skia/web/CanvasKitWebGLBufferImpl.js.map +1 -0
- package/lib/commonjs/skia/web/JsiSkImageFactory.js +8 -2
- package/lib/commonjs/skia/web/JsiSkImageFactory.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkia.js +2 -3
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/commonjs/skia/web/JsiVideo.d.ts +24 -0
- package/lib/commonjs/skia/web/JsiVideo.js +83 -0
- package/lib/commonjs/skia/web/JsiVideo.js.map +1 -0
- package/lib/commonjs/views/SkiaDomView.js +2 -0
- package/lib/commonjs/views/SkiaDomView.js.map +1 -1
- package/lib/commonjs/views/SkiaDomView.web.js +2 -0
- package/lib/commonjs/views/SkiaDomView.web.js.map +1 -1
- package/lib/commonjs/views/SkiaJSDomView.js +2 -0
- package/lib/commonjs/views/SkiaJSDomView.js.map +1 -1
- package/lib/module/dom/nodes/datatypes/Fitting.js +41 -29
- package/lib/module/dom/nodes/datatypes/Fitting.js.map +1 -1
- package/lib/module/external/reanimated/useVideo.d.ts +16 -4
- package/lib/module/external/reanimated/useVideo.js +92 -17
- package/lib/module/external/reanimated/useVideo.js.map +1 -1
- package/lib/module/external/reanimated/useVideoLoading.d.ts +4 -0
- package/lib/module/external/reanimated/useVideoLoading.js +20 -0
- package/lib/module/external/reanimated/useVideoLoading.js.map +1 -0
- package/lib/module/external/reanimated/useVideoLoading.web.d.ts +4 -0
- package/lib/module/external/reanimated/useVideoLoading.web.js +13 -0
- package/lib/module/external/reanimated/useVideoLoading.web.js.map +1 -0
- package/lib/module/renderer/components/shapes/FitBox.d.ts +2 -10
- package/lib/module/renderer/components/shapes/FitBox.js +32 -3
- package/lib/module/renderer/components/shapes/FitBox.js.map +1 -1
- package/lib/module/skia/core/Matrix.js +5 -1
- package/lib/module/skia/core/Matrix.js.map +1 -1
- package/lib/module/skia/types/Matrix.js +2 -0
- package/lib/module/skia/types/Matrix.js.map +1 -1
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +3 -1
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js +2 -1
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
- package/lib/module/skia/types/Skia.d.ts +1 -1
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/Video/Video.d.ts +9 -1
- package/lib/module/skia/types/Video/Video.js.map +1 -1
- package/lib/module/skia/web/CanvasKitWebGLBufferImpl.d.ts +9 -0
- package/lib/module/skia/web/CanvasKitWebGLBufferImpl.js +23 -0
- package/lib/module/skia/web/CanvasKitWebGLBufferImpl.js.map +1 -0
- package/lib/module/skia/web/JsiSkImageFactory.js +9 -3
- package/lib/module/skia/web/JsiSkImageFactory.js.map +1 -1
- package/lib/module/skia/web/JsiSkia.js +2 -3
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/skia/web/JsiVideo.d.ts +24 -0
- package/lib/module/skia/web/JsiVideo.js +75 -0
- package/lib/module/skia/web/JsiVideo.js.map +1 -0
- package/lib/module/views/SkiaDomView.js +2 -0
- package/lib/module/views/SkiaDomView.js.map +1 -1
- package/lib/module/views/SkiaDomView.web.js +2 -0
- package/lib/module/views/SkiaDomView.web.js.map +1 -1
- package/lib/module/views/SkiaJSDomView.js +2 -0
- package/lib/module/views/SkiaJSDomView.js.map +1 -1
- package/lib/typescript/src/external/reanimated/useVideo.d.ts +16 -4
- package/lib/typescript/src/external/reanimated/useVideoLoading.d.ts +4 -0
- package/lib/typescript/src/external/reanimated/useVideoLoading.web.d.ts +4 -0
- package/lib/typescript/src/renderer/components/shapes/FitBox.d.ts +2 -10
- package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +3 -1
- package/lib/typescript/src/skia/types/Skia.d.ts +1 -1
- package/lib/typescript/src/skia/types/Video/Video.d.ts +9 -1
- package/lib/typescript/src/skia/web/CanvasKitWebGLBufferImpl.d.ts +9 -0
- package/lib/typescript/src/skia/web/JsiVideo.d.ts +24 -0
- package/package.json +1 -1
- package/src/dom/nodes/datatypes/Fitting.ts +28 -21
- package/src/external/reanimated/useVideo.ts +108 -31
- package/src/external/reanimated/useVideoLoading.ts +28 -0
- package/src/external/reanimated/useVideoLoading.web.ts +17 -0
- package/src/renderer/components/shapes/FitBox.tsx +38 -4
- package/src/skia/core/Matrix.ts +4 -2
- package/src/skia/types/Matrix.ts +1 -0
- package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +10 -2
- package/src/skia/types/Skia.ts +1 -1
- package/src/skia/types/Video/Video.ts +7 -1
- package/src/skia/web/CanvasKitWebGLBufferImpl.ts +22 -0
- package/src/skia/web/JsiSkImageFactory.ts +16 -3
- package/src/skia/web/JsiSkia.ts +2 -3
- package/src/skia/web/JsiVideo.ts +96 -0
- package/src/views/SkiaDomView.tsx +4 -0
- package/src/views/SkiaDomView.web.tsx +4 -0
- package/src/views/SkiaJSDomView.tsx +4 -0
- package/lib/commonjs/external/reanimated/video.d.ts +0 -16
- package/lib/commonjs/external/reanimated/video.js +0 -54
- package/lib/commonjs/external/reanimated/video.js.map +0 -1
- package/lib/module/external/reanimated/video.d.ts +0 -16
- package/lib/module/external/reanimated/video.js +0 -46
- package/lib/module/external/reanimated/video.js.map +0 -1
- package/lib/typescript/src/external/reanimated/video.d.ts +0 -16
- 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", "(
|
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.
|
7
|
-
import android.media.
|
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
|
-
|
61
|
-
|
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(
|
120
|
-
//
|
121
|
-
|
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();
|
package/cpp/api/JsiVideo.h
CHANGED
@@ -53,18 +53,43 @@ public:
|
|
53
53
|
return jsi::Value::undefined();
|
54
54
|
}
|
55
55
|
|
56
|
-
JSI_HOST_FUNCTION(
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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)
|
package/cpp/rnskia/RNSkVideo.h
CHANGED
@@ -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
|
-
|
24
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
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::
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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 =
|
38
|
-
if (time.timescale
|
39
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
87
|
-
*timeStamp = CMTimeGetSeconds(time);
|
69
|
+
*timeStamp = CMTimeGetSeconds(currentTime);
|
88
70
|
}
|
89
71
|
|
90
|
-
|
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 =
|
89
|
+
rotationAngle = 90;
|
108
90
|
} else if (transform.a == 0 && transform.b == -1 && transform.c == 1 &&
|
109
91
|
transform.d == 0) {
|
110
|
-
rotationAngle =
|
92
|
+
rotationAngle = 270;
|
111
93
|
} else if (transform.a == -1 && transform.b == 0 && transform.c == 0 &&
|
112
94
|
transform.d == -1) {
|
113
|
-
rotationAngle =
|
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
|
-
|
119
|
-
return rotationAngle * 180 / M_PI;
|
97
|
+
return rotationAngle;
|
120
98
|
}
|
121
99
|
|
122
100
|
void RNSkiOSVideo::seek(double timeInMilliseconds) {
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
10
|
-
|
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
|