@shopify/react-native-skia 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +72 -1
- package/android/cpp/rnskia-android/RNSkAndroidVideo.h +5 -0
- package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +89 -8
- package/cpp/api/JsiVideo.h +37 -5
- package/cpp/rnskia/RNSkVideo.h +5 -0
- package/ios/RNSkia-iOS/RNSkiOSVideo.h +14 -3
- package/ios/RNSkia-iOS/RNSkiOSVideo.mm +78 -60
- 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 +14 -5
- package/lib/commonjs/external/reanimated/useVideo.js +90 -58
- package/lib/commonjs/external/reanimated/useVideo.js.map +1 -1
- 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/Video/Video.d.ts +9 -0
- package/lib/commonjs/skia/types/Video/Video.js.map +1 -1
- package/lib/commonjs/skia/types/index.d.ts +1 -0
- package/lib/commonjs/skia/types/index.js +11 -0
- package/lib/commonjs/skia/types/index.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 +14 -5
- package/lib/module/external/reanimated/useVideo.js +91 -59
- package/lib/module/external/reanimated/useVideo.js.map +1 -1
- 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/Video/Video.d.ts +9 -0
- package/lib/module/skia/types/Video/Video.js.map +1 -1
- package/lib/module/skia/types/index.d.ts +1 -0
- package/lib/module/skia/types/index.js +1 -0
- package/lib/module/skia/types/index.js.map +1 -1
- package/lib/typescript/src/external/reanimated/useVideo.d.ts +14 -5
- package/lib/typescript/src/renderer/components/shapes/FitBox.d.ts +2 -10
- package/lib/typescript/src/skia/types/Video/Video.d.ts +9 -0
- package/lib/typescript/src/skia/types/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/dom/nodes/datatypes/Fitting.ts +28 -21
- package/src/external/reanimated/useVideo.ts +86 -73
- 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/Video/Video.ts +7 -0
- package/src/skia/types/index.ts +1 -0
@@ -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
|
|
@@ -67,6 +68,7 @@ double RNSkAndroidVideo::duration() {
|
|
67
68
|
}
|
68
69
|
return env->CallDoubleMethod(_jniVideo.get(), mid);
|
69
70
|
}
|
71
|
+
|
70
72
|
double RNSkAndroidVideo::framerate() {
|
71
73
|
JNIEnv *env = facebook::jni::Environment::current();
|
72
74
|
jclass cls = env->GetObjectClass(_jniVideo.get());
|
@@ -81,7 +83,7 @@ double RNSkAndroidVideo::framerate() {
|
|
81
83
|
void RNSkAndroidVideo::seek(double timestamp) {
|
82
84
|
JNIEnv *env = facebook::jni::Environment::current();
|
83
85
|
jclass cls = env->GetObjectClass(_jniVideo.get());
|
84
|
-
jmethodID mid = env->GetMethodID(cls, "seek", "(
|
86
|
+
jmethodID mid = env->GetMethodID(cls, "seek", "(D)V");
|
85
87
|
if (!mid) {
|
86
88
|
RNSkLogger::logToConsole("seek method not found");
|
87
89
|
return;
|
@@ -89,4 +91,73 @@ void RNSkAndroidVideo::seek(double timestamp) {
|
|
89
91
|
env->CallVoidMethod(_jniVideo.get(), mid, static_cast<jlong>(timestamp));
|
90
92
|
}
|
91
93
|
|
94
|
+
float RNSkAndroidVideo::getRotationInDegrees() {
|
95
|
+
JNIEnv *env = facebook::jni::Environment::current();
|
96
|
+
jclass cls = env->GetObjectClass(_jniVideo.get());
|
97
|
+
jmethodID mid = env->GetMethodID(cls, "getRotationDegrees", "()I");
|
98
|
+
if (!mid) {
|
99
|
+
RNSkLogger::logToConsole("getRotationDegrees method not found");
|
100
|
+
return 0;
|
101
|
+
}
|
102
|
+
auto rotation = env->CallIntMethod(_jniVideo.get(), mid);
|
103
|
+
return static_cast<float>(rotation);
|
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
|
+
}
|
92
163
|
} // namespace RNSkia
|
@@ -31,6 +31,11 @@ public:
|
|
31
31
|
double duration() override;
|
32
32
|
double framerate() override;
|
33
33
|
void seek(double timestamp) override;
|
34
|
+
float getRotationInDegrees() override;
|
35
|
+
SkISize getSize() override;
|
36
|
+
void play() override;
|
37
|
+
void pause() override;
|
38
|
+
void setVolume(float volume) override;
|
34
39
|
};
|
35
40
|
|
36
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,8 +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;
|
39
|
+
private int rotationDegrees = 0;
|
40
|
+
private int width = 0;
|
41
|
+
private int height = 0;
|
42
|
+
|
43
|
+
private boolean isPlaying = false;
|
32
44
|
|
33
45
|
RNSkVideo(Context context, String localUri) {
|
34
46
|
this.uri = Uri.parse(localUri);
|
@@ -46,6 +58,18 @@ public class RNSkVideo {
|
|
46
58
|
}
|
47
59
|
extractor.selectTrack(trackIndex);
|
48
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
|
+
|
49
73
|
// Retrieve and store video properties
|
50
74
|
if (format.containsKey(MediaFormat.KEY_DURATION)) {
|
51
75
|
durationMs = format.getLong(MediaFormat.KEY_DURATION) / 1000; // Convert microseconds to milliseconds
|
@@ -53,8 +77,11 @@ public class RNSkVideo {
|
|
53
77
|
if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
|
54
78
|
frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
|
55
79
|
}
|
56
|
-
|
57
|
-
|
80
|
+
if (format.containsKey(MediaFormat.KEY_ROTATION)) {
|
81
|
+
rotationDegrees = format.getInteger(MediaFormat.KEY_ROTATION);
|
82
|
+
}
|
83
|
+
width = format.getInteger(MediaFormat.KEY_WIDTH);
|
84
|
+
height = format.getInteger(MediaFormat.KEY_HEIGHT);
|
58
85
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
59
86
|
imageReader = ImageReader.newInstance(
|
60
87
|
width,
|
@@ -87,11 +114,15 @@ public class RNSkVideo {
|
|
87
114
|
}
|
88
115
|
|
89
116
|
@DoNotStrip
|
90
|
-
|
91
117
|
public double getFrameRate() {
|
92
118
|
return frameRate;
|
93
119
|
}
|
94
120
|
|
121
|
+
@DoNotStrip
|
122
|
+
public int getRotationDegrees() {
|
123
|
+
return rotationDegrees;
|
124
|
+
}
|
125
|
+
|
95
126
|
@DoNotStrip
|
96
127
|
public HardwareBuffer nextImage() {
|
97
128
|
if (!decoderOutputAvailable()) {
|
@@ -108,15 +139,38 @@ public class RNSkVideo {
|
|
108
139
|
}
|
109
140
|
|
110
141
|
@DoNotStrip
|
111
|
-
public void seek(
|
112
|
-
//
|
113
|
-
|
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
|
+
|
114
153
|
// Flush the codec to reset internal state and buffers
|
115
154
|
if (decoder != null) {
|
116
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
|
+
}
|
117
166
|
}
|
118
167
|
}
|
119
168
|
|
169
|
+
@DoNotStrip
|
170
|
+
public Point getSize() {
|
171
|
+
return new Point(width, height);
|
172
|
+
}
|
173
|
+
|
120
174
|
private int selectVideoTrack(MediaExtractor extractor) {
|
121
175
|
int numTracks = extractor.getTrackCount();
|
122
176
|
for (int i = 0; i < numTracks; i++) {
|
@@ -171,7 +225,34 @@ public class RNSkVideo {
|
|
171
225
|
}
|
172
226
|
}
|
173
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
|
+
|
174
251
|
public void release() {
|
252
|
+
if (mediaPlayer != null) {
|
253
|
+
mediaPlayer.release();
|
254
|
+
mediaPlayer = null;
|
255
|
+
}
|
175
256
|
if (decoder != null) {
|
176
257
|
decoder.stop();
|
177
258
|
decoder.release();
|
package/cpp/api/JsiVideo.h
CHANGED
@@ -53,11 +53,43 @@ public:
|
|
53
53
|
return jsi::Value::undefined();
|
54
54
|
}
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
JSI_HOST_FUNCTION(rotation) {
|
57
|
+
auto context = getContext();
|
58
|
+
auto rot = getObject()->getRotationInDegrees();
|
59
|
+
return jsi::Value(static_cast<double>(rot));
|
60
|
+
}
|
61
|
+
|
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))
|
61
93
|
|
62
94
|
JsiVideo(std::shared_ptr<RNSkPlatformContext> context,
|
63
95
|
std::shared_ptr<RNSkVideo> video)
|
package/cpp/rnskia/RNSkVideo.h
CHANGED
@@ -18,6 +18,11 @@ public:
|
|
18
18
|
virtual double duration() = 0;
|
19
19
|
virtual double framerate() = 0;
|
20
20
|
virtual void seek(double timestamp) = 0;
|
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;
|
21
26
|
};
|
22
27
|
|
23
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,12 +21,17 @@ 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
|
-
|
30
|
+
float _videoWidth = 0;
|
31
|
+
float _videoHeight = 0;
|
32
|
+
CGAffineTransform _preferredTransform;
|
33
|
+
bool _isPlaying = false;
|
34
|
+
void setupPlayer();
|
29
35
|
NSDictionary *getOutputSettings();
|
30
36
|
|
31
37
|
public:
|
@@ -35,6 +41,11 @@ public:
|
|
35
41
|
double duration() override;
|
36
42
|
double framerate() override;
|
37
43
|
void seek(double timestamp) override;
|
44
|
+
void play();
|
45
|
+
void pause();
|
46
|
+
float getRotationInDegrees() override;
|
47
|
+
SkISize getSize() override;
|
48
|
+
void setVolume(float volume);
|
38
49
|
};
|
39
50
|
|
40
51
|
} // namespace RNSkia
|
@@ -1,5 +1,3 @@
|
|
1
|
-
#pragma once
|
2
|
-
|
3
1
|
#include <memory>
|
4
2
|
#include <string>
|
5
3
|
|
@@ -18,77 +16,60 @@ namespace RNSkia {
|
|
18
16
|
|
19
17
|
RNSkiOSVideo::RNSkiOSVideo(std::string url, RNSkPlatformContext *context)
|
20
18
|
: _url(std::move(url)), _context(context) {
|
21
|
-
|
19
|
+
setupPlayer();
|
22
20
|
}
|
23
21
|
|
24
|
-
RNSkiOSVideo::~RNSkiOSVideo() {
|
22
|
+
RNSkiOSVideo::~RNSkiOSVideo() {
|
23
|
+
if (_player) {
|
24
|
+
[_player pause];
|
25
|
+
}
|
26
|
+
}
|
25
27
|
|
26
|
-
void RNSkiOSVideo::
|
27
|
-
|
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;
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
error:&error];
|
34
|
-
if (error) {
|
35
|
-
NSLog(@"Error initializing asset reader: %@", error.localizedDescription);
|
36
|
-
return;
|
37
|
-
}
|
34
|
+
NSDictionary *outputSettings = getOutputSettings();
|
35
|
+
_videoOutput =
|
36
|
+
[[AVPlayerItemVideoOutput alloc] initWithOutputSettings:outputSettings];
|
37
|
+
[playerItem addOutput:_videoOutput];
|
38
38
|
|
39
|
-
CMTime time =
|
40
|
-
if (time.timescale
|
41
|
-
|
42
|
-
return;
|
39
|
+
CMTime time = playerItem.asset.duration;
|
40
|
+
if (time.timescale != 0) {
|
41
|
+
_duration = CMTimeGetSeconds(time) * 1000; // Store duration in milliseconds
|
43
42
|
}
|
44
43
|
|
45
|
-
_duration = CMTimeGetSeconds(time) * 1000; // Store duration in milliseconds
|
46
44
|
AVAssetTrack *videoTrack =
|
47
|
-
[[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
assetReader.timeRange = timeRange;
|
56
|
-
if ([assetReader canAddOutput:trackOutput]) {
|
57
|
-
[assetReader addOutput:trackOutput];
|
58
|
-
[assetReader startReading];
|
59
|
-
} else {
|
60
|
-
NSLog(@"Cannot add output to asset reader.");
|
61
|
-
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;
|
62
52
|
}
|
63
|
-
|
64
|
-
_reader = assetReader;
|
65
|
-
_trackOutput = trackOutput;
|
53
|
+
play();
|
66
54
|
}
|
67
55
|
|
68
56
|
sk_sp<SkImage> RNSkiOSVideo::nextImage(double *timeStamp) {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
}
|
74
|
-
|
75
|
-
// Extract the pixel buffer from the sample buffer
|
76
|
-
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
57
|
+
CMTime currentTime = [_player currentTime];
|
58
|
+
CVPixelBufferRef pixelBuffer =
|
59
|
+
[_videoOutput copyPixelBufferForItemTime:currentTime
|
60
|
+
itemTimeForDisplay:nullptr];
|
77
61
|
if (!pixelBuffer) {
|
78
62
|
NSLog(@"No pixel buffer.");
|
79
|
-
CFRelease(sampleBuffer);
|
80
63
|
return nullptr;
|
81
64
|
}
|
82
65
|
|
83
|
-
auto skImage = _context->makeImageFromNativeBuffer(
|
84
|
-
reinterpret_cast<void *>(pixelBuffer));
|
66
|
+
auto skImage = _context->makeImageFromNativeBuffer((void *)pixelBuffer);
|
85
67
|
|
86
68
|
if (timeStamp) {
|
87
|
-
|
88
|
-
*timeStamp = CMTimeGetSeconds(time);
|
69
|
+
*timeStamp = CMTimeGetSeconds(currentTime);
|
89
70
|
}
|
90
71
|
|
91
|
-
|
72
|
+
CVPixelBufferRelease(pixelBuffer);
|
92
73
|
return skImage;
|
93
74
|
}
|
94
75
|
|
@@ -99,21 +80,58 @@ NSDictionary *RNSkiOSVideo::getOutputSettings() {
|
|
99
80
|
};
|
100
81
|
}
|
101
82
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
83
|
+
float RNSkiOSVideo::getRotationInDegrees() {
|
84
|
+
CGFloat rotationAngle = 0.0;
|
85
|
+
auto transform = _preferredTransform;
|
86
|
+
// Determine the rotation angle in radians
|
87
|
+
if (transform.a == 0 && transform.b == 1 && transform.c == -1 &&
|
88
|
+
transform.d == 0) {
|
89
|
+
rotationAngle = 90;
|
90
|
+
} else if (transform.a == 0 && transform.b == -1 && transform.c == 1 &&
|
91
|
+
transform.d == 0) {
|
92
|
+
rotationAngle = 270;
|
93
|
+
} else if (transform.a == -1 && transform.b == 0 && transform.c == 0 &&
|
94
|
+
transform.d == -1) {
|
95
|
+
rotationAngle = 180;
|
107
96
|
}
|
97
|
+
return rotationAngle;
|
98
|
+
}
|
108
99
|
|
109
|
-
|
100
|
+
void RNSkiOSVideo::seek(double timeInMilliseconds) {
|
101
|
+
CMTime seekTime =
|
110
102
|
CMTimeMakeWithSeconds(timeInMilliseconds / 1000.0, NSEC_PER_SEC);
|
111
|
-
|
112
|
-
|
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;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
void RNSkiOSVideo::pause() {
|
121
|
+
if (_player) {
|
122
|
+
[_player pause];
|
123
|
+
_isPlaying = false;
|
124
|
+
}
|
113
125
|
}
|
114
126
|
|
115
127
|
double RNSkiOSVideo::duration() { return _duration; }
|
116
128
|
|
117
129
|
double RNSkiOSVideo::framerate() { return _framerate; }
|
118
130
|
|
131
|
+
SkISize RNSkiOSVideo::getSize() {
|
132
|
+
return SkISize::Make(_videoWidth, _videoHeight);
|
133
|
+
}
|
134
|
+
|
135
|
+
void RNSkiOSVideo::setVolume(float volume) { _player.volume = volume; }
|
136
|
+
|
119
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
|