@shopify/react-native-skia 1.3.1 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|