@shopify/react-native-skia 1.2.3 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/android/CMakeLists.txt +1 -0
  2. package/android/cpp/jni/JniPlatformContext.cpp +23 -0
  3. package/android/cpp/jni/include/JniPlatformContext.h +2 -0
  4. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +6 -0
  5. package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +92 -0
  6. package/android/cpp/rnskia-android/RNSkAndroidVideo.h +36 -0
  7. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +3 -3
  8. package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java +5 -0
  9. package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +185 -0
  10. package/cpp/api/JsiSkApi.h +2 -0
  11. package/cpp/api/JsiVideo.h +87 -0
  12. package/cpp/rnskia/RNSkPlatformContext.h +3 -0
  13. package/cpp/rnskia/RNSkVideo.h +23 -0
  14. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.h +2 -0
  15. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +6 -0
  16. package/ios/RNSkia-iOS/RNSkiOSVideo.h +40 -0
  17. package/ios/RNSkia-iOS/RNSkiOSVideo.mm +119 -0
  18. package/ios/RNSkia-iOS/RNSkiOSView.mm +37 -0
  19. package/lib/commonjs/external/reanimated/index.d.ts +1 -0
  20. package/lib/commonjs/external/reanimated/index.js +11 -0
  21. package/lib/commonjs/external/reanimated/index.js.map +1 -1
  22. package/lib/commonjs/external/reanimated/useVideo.d.ts +12 -0
  23. package/lib/commonjs/external/reanimated/useVideo.js +105 -0
  24. package/lib/commonjs/external/reanimated/useVideo.js.map +1 -0
  25. package/lib/commonjs/mock/index.js +2 -1
  26. package/lib/commonjs/mock/index.js.map +1 -1
  27. package/lib/commonjs/skia/types/Skia.d.ts +2 -0
  28. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  29. package/lib/commonjs/skia/types/Video/Video.d.ts +8 -0
  30. package/lib/commonjs/skia/types/Video/Video.js +6 -0
  31. package/lib/commonjs/skia/types/Video/Video.js.map +1 -0
  32. package/lib/commonjs/skia/types/Video/index.d.ts +1 -0
  33. package/lib/commonjs/skia/types/Video/index.js +17 -0
  34. package/lib/commonjs/skia/types/Video/index.js.map +1 -0
  35. package/lib/commonjs/skia/web/JsiSkia.js +4 -1
  36. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  37. package/lib/module/external/reanimated/index.d.ts +1 -0
  38. package/lib/module/external/reanimated/index.js +1 -0
  39. package/lib/module/external/reanimated/index.js.map +1 -1
  40. package/lib/module/external/reanimated/useVideo.d.ts +12 -0
  41. package/lib/module/external/reanimated/useVideo.js +97 -0
  42. package/lib/module/external/reanimated/useVideo.js.map +1 -0
  43. package/lib/module/mock/index.js +2 -1
  44. package/lib/module/mock/index.js.map +1 -1
  45. package/lib/module/skia/types/Skia.d.ts +2 -0
  46. package/lib/module/skia/types/Skia.js.map +1 -1
  47. package/lib/module/skia/types/Video/Video.d.ts +8 -0
  48. package/lib/module/skia/types/Video/Video.js +2 -0
  49. package/lib/module/skia/types/Video/Video.js.map +1 -0
  50. package/lib/module/skia/types/Video/index.d.ts +1 -0
  51. package/lib/module/skia/types/Video/index.js +2 -0
  52. package/lib/module/skia/types/Video/index.js.map +1 -0
  53. package/lib/module/skia/web/JsiSkia.js +4 -1
  54. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  55. package/lib/typescript/src/external/reanimated/index.d.ts +1 -0
  56. package/lib/typescript/src/external/reanimated/useVideo.d.ts +12 -0
  57. package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
  58. package/lib/typescript/src/skia/types/Video/Video.d.ts +8 -0
  59. package/lib/typescript/src/skia/types/Video/index.d.ts +1 -0
  60. package/package.json +1 -1
  61. package/scripts/setup-canvaskit.js +1 -1
  62. package/src/external/reanimated/index.ts +1 -0
  63. package/src/external/reanimated/useVideo.ts +130 -0
  64. package/src/mock/index.ts +1 -0
  65. package/src/skia/types/Skia.ts +2 -0
  66. package/src/skia/types/Video/Video.ts +9 -0
  67. package/src/skia/types/Video/index.ts +1 -0
  68. package/src/skia/web/JsiSkia.ts +3 -0
@@ -48,6 +48,7 @@ add_library(
48
48
  "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp"
49
49
  "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/GrAHardwareBufferUtils.cpp"
50
50
  "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/AHardwareBufferUtils.cpp"
51
+ "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/RNSkAndroidVideo.cpp"
51
52
 
52
53
  "${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiHostObject.cpp"
53
54
  "${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiValue.cpp"
@@ -75,6 +75,29 @@ TSelf JniPlatformContext::initHybrid(jni::alias_ref<jhybridobject> jThis,
75
75
  return makeCxxInstance(jThis, pixelDensity);
76
76
  }
77
77
 
78
+ jni::global_ref<jobject>
79
+ JniPlatformContext::createVideo(const std::string &url) {
80
+ jni::ThreadScope ts; // Manages JNI thread attachment/detachment
81
+
82
+ // Get the JNI environment
83
+ JNIEnv *env = facebook::jni::Environment::current();
84
+
85
+ // Convert std::string to jstring
86
+ jstring jUrl = env->NewStringUTF(url.c_str());
87
+
88
+ // Get the method ID for the createVideo method
89
+ // Replace "Lcom/yourpackage/RNSkVideo;" with the actual return type
90
+ // descriptor
91
+ static auto method =
92
+ javaPart_->getClass()->getMethod<jobject(jstring)>("createVideo");
93
+
94
+ // Call the method and receive a local reference to the video object
95
+ auto videoObject = method(javaPart_.get(), jUrl);
96
+ // Clean up the jstring local reference
97
+ auto result = jni::make_global(videoObject);
98
+ return result;
99
+ }
100
+
78
101
  sk_sp<SkImage> JniPlatformContext::takeScreenshotFromViewTag(size_t tag) {
79
102
  // Call the java method for creating a view screenshot as a bitmap:
80
103
  auto env = jni::Environment::current();
@@ -50,6 +50,8 @@ public:
50
50
  _onNotifyDrawLoop = callback;
51
51
  }
52
52
 
53
+ jni::global_ref<jobject> createVideo(const std::string &url);
54
+
53
55
  private:
54
56
  friend HybridBase;
55
57
  jni::global_ref<JniPlatformContext::javaobject> javaPart_;
@@ -11,6 +11,7 @@
11
11
 
12
12
  #include "AHardwareBufferUtils.h"
13
13
  #include "JniPlatformContext.h"
14
+ #include "RNSkAndroidVideo.h"
14
15
  #include "RNSkPlatformContext.h"
15
16
  #include "SkiaOpenGLSurfaceFactory.h"
16
17
 
@@ -57,6 +58,11 @@ public:
57
58
  return SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(buffer);
58
59
  }
59
60
 
61
+ std::shared_ptr<RNSkVideo> createVideo(const std::string &url) override {
62
+ auto jniVideo = _jniPlatformContext->createVideo(url);
63
+ return std::make_shared<RNSkAndroidVideo>(jniVideo);
64
+ }
65
+
60
66
  void releaseNativeBuffer(uint64_t pointer) override {
61
67
  #if __ANDROID_API__ >= 26
62
68
  AHardwareBuffer *buffer = reinterpret_cast<AHardwareBuffer *>(pointer);
@@ -0,0 +1,92 @@
1
+ #include <memory>
2
+ #include <string>
3
+
4
+ #if __ANDROID_API__ >= 26
5
+ #include <android/hardware_buffer_jni.h>
6
+ #endif
7
+
8
+ #pragma clang diagnostic push
9
+ #pragma clang diagnostic ignored "-Wdocumentation"
10
+
11
+ #include "include/core/SkImage.h"
12
+
13
+ #pragma clang diagnostic pop
14
+
15
+ #include "RNSkAndroidVideo.h"
16
+ #include "SkiaOpenGLSurfaceFactory.h"
17
+
18
+ namespace RNSkia {
19
+
20
+ namespace jsi = facebook::jsi;
21
+ namespace jni = facebook::jni;
22
+
23
+ RNSkAndroidVideo::RNSkAndroidVideo(jni::global_ref<jobject> jniVideo)
24
+ : _jniVideo(jniVideo) {
25
+ #if __ANDROID_API__ < 26
26
+ throw std::runtime_error("Skia Videos are only support on API 26 and above");
27
+ #endif
28
+ }
29
+
30
+ RNSkAndroidVideo::~RNSkAndroidVideo() {}
31
+
32
+ sk_sp<SkImage> RNSkAndroidVideo::nextImage(double *timeStamp) {
33
+ #if __ANDROID_API__ >= 26
34
+ JNIEnv *env = facebook::jni::Environment::current();
35
+ // Get the Java class and method ID
36
+ jclass cls = env->GetObjectClass(_jniVideo.get());
37
+ jmethodID mid =
38
+ env->GetMethodID(cls, "nextImage", "()Landroid/hardware/HardwareBuffer;");
39
+ if (mid == nullptr) {
40
+ // Method not found, handle error
41
+ RNSkLogger::logToConsole("nextImage method not found");
42
+ return nullptr;
43
+ }
44
+ // Call the Java method
45
+ jobject jHardwareBuffer = env->CallObjectMethod(_jniVideo.get(), mid);
46
+ if (jHardwareBuffer == nullptr) {
47
+ RNSkLogger::logToConsole("Buffer not found");
48
+ // Null return, handle error
49
+ return nullptr;
50
+ }
51
+ // Convert jobject to AHardwareBuffer
52
+ AHardwareBuffer *buffer =
53
+ AHardwareBuffer_fromHardwareBuffer(env, jHardwareBuffer);
54
+ return SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(buffer);
55
+ #else
56
+ return nullptr;
57
+ #endif
58
+ }
59
+
60
+ double RNSkAndroidVideo::duration() {
61
+ JNIEnv *env = facebook::jni::Environment::current();
62
+ jclass cls = env->GetObjectClass(_jniVideo.get());
63
+ jmethodID mid = env->GetMethodID(cls, "getDuration", "()D");
64
+ if (!mid) {
65
+ RNSkLogger::logToConsole("getDuration method not found");
66
+ return 0.0;
67
+ }
68
+ return env->CallDoubleMethod(_jniVideo.get(), mid);
69
+ }
70
+ double RNSkAndroidVideo::framerate() {
71
+ JNIEnv *env = facebook::jni::Environment::current();
72
+ jclass cls = env->GetObjectClass(_jniVideo.get());
73
+ jmethodID mid = env->GetMethodID(cls, "getFrameRate", "()D");
74
+ if (!mid) {
75
+ RNSkLogger::logToConsole("getFrameRate method not found");
76
+ return 0.0;
77
+ }
78
+ return env->CallDoubleMethod(_jniVideo.get(), mid);
79
+ }
80
+
81
+ void RNSkAndroidVideo::seek(double timestamp) {
82
+ JNIEnv *env = facebook::jni::Environment::current();
83
+ jclass cls = env->GetObjectClass(_jniVideo.get());
84
+ jmethodID mid = env->GetMethodID(cls, "seek", "(J)V");
85
+ if (!mid) {
86
+ RNSkLogger::logToConsole("seek method not found");
87
+ return;
88
+ }
89
+ env->CallVoidMethod(_jniVideo.get(), mid, static_cast<jlong>(timestamp));
90
+ }
91
+
92
+ } // namespace RNSkia
@@ -0,0 +1,36 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ #include <fbjni/fbjni.h>
6
+ #include <jni.h>
7
+ #include <jsi/jsi.h>
8
+
9
+ #pragma clang diagnostic push
10
+ #pragma clang diagnostic ignored "-Wdocumentation"
11
+
12
+ #include "include/core/SkImage.h"
13
+
14
+ #pragma clang diagnostic pop
15
+
16
+ #include "RNSkVideo.h"
17
+
18
+ namespace RNSkia {
19
+
20
+ namespace jsi = facebook::jsi;
21
+ namespace jni = facebook::jni;
22
+
23
+ class RNSkAndroidVideo : public RNSkVideo {
24
+ private:
25
+ jni::global_ref<jobject> _jniVideo;
26
+
27
+ public:
28
+ explicit RNSkAndroidVideo(jni::global_ref<jobject> jniVideo);
29
+ ~RNSkAndroidVideo();
30
+ sk_sp<SkImage> nextImage(double *timeStamp = nullptr) override;
31
+ double duration() override;
32
+ double framerate() override;
33
+ void seek(double timestamp) override;
34
+ };
35
+
36
+ } // namespace RNSkia
@@ -37,7 +37,6 @@ SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(void *buffer,
37
37
  // TODO: find out if we can detect, which graphic buffers support
38
38
  // GR_GL_TEXTURE_2D
39
39
  case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
40
- case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
41
40
  format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL);
42
41
  case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
43
42
  format = GrBackendFormats::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_EXTERNAL);
@@ -64,9 +63,10 @@ SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(void *buffer,
64
63
  const_cast<AHardwareBuffer *>(hardwareBuffer), description.width,
65
64
  description.height, &deleteImageProc, &updateImageProc, &deleteImageCtx,
66
65
  false, format, false);
67
- if (!backendTex.isValid()) [[unlikely]] {
68
- throw std::runtime_error(
66
+ if (!backendTex.isValid()) {
67
+ RNSkLogger::logToConsole(
69
68
  "Failed to convert HardwareBuffer to OpenGL Texture!");
69
+ return nullptr;
70
70
  }
71
71
  sk_sp<SkImage> image = SkImages::BorrowTextureFrom(
72
72
  ThreadContextHolder::ThreadSkiaOpenGLContext.directContext.get(),
@@ -38,6 +38,11 @@ public class PlatformContext {
38
38
  mHybridData = initHybrid(reactContext.getResources().getDisplayMetrics().density);
39
39
  }
40
40
 
41
+ @DoNotStrip
42
+ public Object createVideo(String url) {
43
+ return new RNSkVideo(mContext, url);
44
+ }
45
+
41
46
  private byte[] getStreamAsBytes(InputStream is) throws IOException {
42
47
  ByteArrayOutputStream buffer = new ByteArrayOutputStream();
43
48
  int nRead;
@@ -0,0 +1,185 @@
1
+ package com.shopify.reactnative.skia;
2
+
3
+ import android.content.Context;
4
+ import android.graphics.ImageFormat;
5
+ import android.hardware.HardwareBuffer;
6
+ import android.media.Image;
7
+ import android.media.ImageReader;
8
+ import android.media.MediaCodec;
9
+ import android.media.MediaExtractor;
10
+ import android.media.MediaFormat;
11
+ import android.net.Uri;
12
+ import android.os.Build;
13
+ import android.view.Surface;
14
+
15
+ import androidx.annotation.RequiresApi;
16
+
17
+ import com.facebook.jni.annotations.DoNotStrip;
18
+
19
+ import java.io.IOException;
20
+ import java.nio.ByteBuffer;
21
+
22
+ public class RNSkVideo {
23
+ private final Uri uri;
24
+ private final Context context;
25
+
26
+ private MediaExtractor extractor;
27
+ private MediaCodec decoder;
28
+ private ImageReader imageReader;
29
+ private Surface outputSurface;
30
+ private double durationMs;
31
+ private double frameRate;
32
+
33
+ RNSkVideo(Context context, String localUri) {
34
+ this.uri = Uri.parse(localUri);
35
+ this.context = context;
36
+ this.initializeReader();
37
+ }
38
+
39
+ private void initializeReader() {
40
+ extractor = new MediaExtractor();
41
+ try {
42
+ extractor.setDataSource(context, this.uri, null);
43
+ int trackIndex = selectVideoTrack(extractor);
44
+ if (trackIndex < 0) {
45
+ throw new RuntimeException("No video track found in " + this.uri);
46
+ }
47
+ extractor.selectTrack(trackIndex);
48
+ MediaFormat format = extractor.getTrackFormat(trackIndex);
49
+ // Retrieve and store video properties
50
+ if (format.containsKey(MediaFormat.KEY_DURATION)) {
51
+ durationMs = format.getLong(MediaFormat.KEY_DURATION) / 1000; // Convert microseconds to milliseconds
52
+ }
53
+ if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
54
+ frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
55
+ }
56
+ int width = format.getInteger(MediaFormat.KEY_WIDTH);
57
+ int height = format.getInteger(MediaFormat.KEY_HEIGHT);
58
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
59
+ imageReader = ImageReader.newInstance(
60
+ width,
61
+ height,
62
+ ImageFormat.PRIVATE,
63
+ 2,
64
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
65
+ );
66
+ } else {
67
+ imageReader = ImageReader.newInstance(width, height,
68
+ ImageFormat.PRIVATE, 2);
69
+ }
70
+ outputSurface = imageReader.getSurface();
71
+
72
+ // Create a decoder for the format
73
+ String mime = format.getString(MediaFormat.KEY_MIME);
74
+ decoder = MediaCodec.createDecoderByType(mime);
75
+
76
+ // Note: Use an output Surface for rendering if necessary, otherwise handle buffers
77
+ decoder.configure(format, outputSurface, null, 0);
78
+ decoder.start();
79
+ } catch (IOException e) {
80
+ throw new RuntimeException("Failed to initialize extractor or decoder", e);
81
+ }
82
+ }
83
+
84
+ @DoNotStrip
85
+ public double getDuration() {
86
+ return durationMs;
87
+ }
88
+
89
+ @DoNotStrip
90
+
91
+ public double getFrameRate() {
92
+ return frameRate;
93
+ }
94
+
95
+ @DoNotStrip
96
+ public HardwareBuffer nextImage() {
97
+ if (!decoderOutputAvailable()) {
98
+ decodeFrame();
99
+ }
100
+
101
+ Image image = imageReader.acquireLatestImage();
102
+ if (image != null) {
103
+ HardwareBuffer hardwareBuffer = image.getHardwareBuffer();
104
+ image.close(); // Make sure to close the Image to free up the buffer
105
+ return hardwareBuffer;
106
+ }
107
+ return null;
108
+ }
109
+
110
+ @DoNotStrip
111
+ public void seek(long timestamp) {
112
+ // Seek to the closest sync frame at or before the specified time
113
+ extractor.seekTo(timestamp * 1000, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
114
+ // Flush the codec to reset internal state and buffers
115
+ if (decoder != null) {
116
+ decoder.flush();
117
+ }
118
+ }
119
+
120
+ private int selectVideoTrack(MediaExtractor extractor) {
121
+ int numTracks = extractor.getTrackCount();
122
+ for (int i = 0; i < numTracks; i++) {
123
+ MediaFormat format = extractor.getTrackFormat(i);
124
+ String mime = format.getString(MediaFormat.KEY_MIME);
125
+ if (mime.startsWith("video/")) {
126
+ return i;
127
+ }
128
+ }
129
+ return -1;
130
+ }
131
+
132
+ private boolean decoderOutputAvailable() {
133
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
134
+ int outputBufferId = decoder.dequeueOutputBuffer(info, 0);
135
+ if (outputBufferId >= 0) {
136
+ // If a buffer is available, release it immediately back since we are just checking
137
+ decoder.releaseOutputBuffer(outputBufferId, true);
138
+ return true;
139
+ }
140
+ return false;
141
+ }
142
+
143
+ private void decodeFrame() {
144
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
145
+ long timeoutUs = 10000;
146
+ boolean isEOS = false;
147
+
148
+ int inputBufferId = decoder.dequeueInputBuffer(timeoutUs);
149
+ if (inputBufferId >= 0) {
150
+ ByteBuffer inputBuffer = decoder.getInputBuffer(inputBufferId);
151
+ int sampleSize = extractor.readSampleData(inputBuffer, 0);
152
+ if (sampleSize < 0) {
153
+ // End of stream, make sure to send this information to the decoder
154
+ decoder.queueInputBuffer(inputBufferId, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
155
+ isEOS = true;
156
+ } else {
157
+ long presentationTimeUs = extractor.getSampleTime();
158
+ decoder.queueInputBuffer(inputBufferId, 0, sampleSize, presentationTimeUs, 0);
159
+ extractor.advance();
160
+ }
161
+ }
162
+
163
+ int outputBufferId = decoder.dequeueOutputBuffer(info, timeoutUs);
164
+ if (outputBufferId >= 0) {
165
+ // If we have a valid buffer, release it to make it available to the ImageReader's surface
166
+ decoder.releaseOutputBuffer(outputBufferId, true);
167
+
168
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
169
+ isEOS = true;
170
+ }
171
+ }
172
+ }
173
+
174
+ public void release() {
175
+ if (decoder != null) {
176
+ decoder.stop();
177
+ decoder.release();
178
+ decoder = null;
179
+ }
180
+ if (extractor != null) {
181
+ extractor.release();
182
+ extractor = null;
183
+ }
184
+ }
185
+ }
@@ -50,6 +50,7 @@
50
50
  #include "JsiSkTypefaceFactory.h"
51
51
  #include "JsiSkTypefaceFontProviderFactory.h"
52
52
  #include "JsiSkVertices.h"
53
+ #include "JsiVideo.h"
53
54
 
54
55
  namespace RNSkia {
55
56
 
@@ -67,6 +68,7 @@ public:
67
68
  // We create the system font manager eagerly since it has proven to be too
68
69
  // slow to do it on demand
69
70
  JsiSkFontMgrFactory::getFontMgr(getContext());
71
+ installFunction("Video", JsiVideo::createCtor(context));
70
72
  installFunction("Font", JsiSkFont::createCtor(context));
71
73
  installFunction("Paint", JsiSkPaint::createCtor(context));
72
74
  installFunction("RSXform", JsiSkRSXform::createCtor(context));
@@ -0,0 +1,87 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <numeric>
5
+ #include <utility>
6
+ #include <vector>
7
+
8
+ #include "JsiSkHostObjects.h"
9
+ #include "RNSkLog.h"
10
+ #include <jsi/jsi.h>
11
+
12
+ #include "JsiSkPaint.h"
13
+ #include "JsiSkPoint.h"
14
+ #include "JsiSkRect.h"
15
+ #include "JsiSkTypeface.h"
16
+
17
+ #include "RNSkVideo.h"
18
+
19
+ #pragma clang diagnostic push
20
+ #pragma clang diagnostic ignored "-Wdocumentation"
21
+
22
+ #include "include/core/SkFont.h"
23
+ #include "include/core/SkFontMetrics.h"
24
+
25
+ #pragma clang diagnostic pop
26
+
27
+ namespace RNSkia {
28
+
29
+ namespace jsi = facebook::jsi;
30
+
31
+ class JsiVideo : public JsiSkWrappingSharedPtrHostObject<RNSkVideo> {
32
+ public:
33
+ EXPORT_JSI_API_TYPENAME(JsiVideo, Video)
34
+
35
+ JSI_HOST_FUNCTION(nextImage) {
36
+ double timestamp = 0;
37
+ auto video = getObject();
38
+ auto image = video->nextImage(&timestamp);
39
+ if (!image) {
40
+ return jsi::Value::null();
41
+ }
42
+ return jsi::Object::createFromHostObject(
43
+ runtime, std::make_shared<JsiSkImage>(getContext(), std::move(image)));
44
+ }
45
+
46
+ JSI_HOST_FUNCTION(duration) { return getObject()->duration(); }
47
+
48
+ JSI_HOST_FUNCTION(framerate) { return getObject()->framerate(); }
49
+
50
+ JSI_HOST_FUNCTION(seek) {
51
+ double timestamp = arguments[0].asNumber();
52
+ getObject()->seek(timestamp);
53
+ return jsi::Value::undefined();
54
+ }
55
+
56
+ JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiVideo, nextImage),
57
+ JSI_EXPORT_FUNC(JsiVideo, duration),
58
+ JSI_EXPORT_FUNC(JsiVideo, framerate),
59
+ JSI_EXPORT_FUNC(JsiVideo, seek),
60
+ JSI_EXPORT_FUNC(JsiVideo, dispose))
61
+
62
+ JsiVideo(std::shared_ptr<RNSkPlatformContext> context,
63
+ std::shared_ptr<RNSkVideo> video)
64
+ : JsiSkWrappingSharedPtrHostObject(std::move(context), std::move(video)) {
65
+ }
66
+
67
+ /**
68
+ * Creates the function for construction a new instance of the SkFont
69
+ * wrapper
70
+ * @param context Platform context
71
+ * @return A function for creating a new host object wrapper for the SkFont
72
+ * class
73
+ */
74
+ static const jsi::HostFunctionType
75
+ createCtor(std::shared_ptr<RNSkPlatformContext> context) {
76
+ return JSI_HOST_FUNCTION_LAMBDA {
77
+ auto url = arguments[0].asString(runtime).utf8(runtime);
78
+ auto video = context->createVideo(url);
79
+ // Return the newly constructed object
80
+ return jsi::Object::createFromHostObject(
81
+ runtime,
82
+ std::make_shared<JsiVideo>(std::move(context), std::move(video)));
83
+ };
84
+ }
85
+ };
86
+
87
+ } // namespace RNSkia
@@ -10,6 +10,7 @@
10
10
  #include <utility>
11
11
 
12
12
  #include "RNSkDispatchQueue.h"
13
+ #include "RNSkVideo.h"
13
14
 
14
15
  #pragma clang diagnostic push
15
16
  #pragma clang diagnostic ignored "-Wdocumentation"
@@ -146,6 +147,8 @@ public:
146
147
 
147
148
  virtual uint64_t makeNativeBuffer(sk_sp<SkImage> image) = 0;
148
149
 
150
+ virtual std::shared_ptr<RNSkVideo> createVideo(const std::string &url) = 0;
151
+
149
152
  /**
150
153
  * Return the Platform specific font manager
151
154
  */
@@ -0,0 +1,23 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ #pragma clang diagnostic push
6
+ #pragma clang diagnostic ignored "-Wdocumentation"
7
+
8
+ #include "include/core/SkImage.h"
9
+
10
+ #pragma clang diagnostic pop
11
+
12
+ namespace RNSkia {
13
+
14
+ class RNSkVideo {
15
+ public:
16
+ virtual ~RNSkVideo() = default;
17
+ virtual sk_sp<SkImage> nextImage(double *timeStamp = nullptr) = 0;
18
+ virtual double duration() = 0;
19
+ virtual double framerate() = 0;
20
+ virtual void seek(double timestamp) = 0;
21
+ };
22
+
23
+ } // namespace RNSkia
@@ -65,6 +65,8 @@ public:
65
65
 
66
66
  void releaseNativeBuffer(uint64_t pointer) override;
67
67
 
68
+ std::shared_ptr<RNSkVideo> createVideo(const std::string &url) override;
69
+
68
70
  virtual void performStreamOperation(
69
71
  const std::string &sourceUri,
70
72
  const std::function<void(std::unique_ptr<SkStreamAsset>)> &op) override;
@@ -5,6 +5,7 @@
5
5
  #include <thread>
6
6
  #include <utility>
7
7
 
8
+ #include "RNSkiOSVideo.h"
8
9
  #import "SkiaCVPixelBufferUtils.h"
9
10
  #import "SkiaMetalSurfaceFactory.h"
10
11
 
@@ -143,6 +144,11 @@ uint64_t RNSkiOSPlatformContext::makeNativeBuffer(sk_sp<SkImage> image) {
143
144
  return reinterpret_cast<uint64_t>(pixelBuffer);
144
145
  }
145
146
 
147
+ std::shared_ptr<RNSkVideo>
148
+ RNSkiOSPlatformContext::createVideo(const std::string &url) {
149
+ return std::make_shared<RNSkiOSVideo>(url, this);
150
+ }
151
+
146
152
  void RNSkiOSPlatformContext::raiseError(const std::exception &err) {
147
153
  RCTFatal(RCTErrorWithMessage([NSString stringWithUTF8String:err.what()]));
148
154
  }
@@ -0,0 +1,40 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ #include <AVFoundation/AVFoundation.h>
6
+ #include <CoreVideo/CoreVideo.h>
7
+
8
+ #pragma clang diagnostic push
9
+ #pragma clang diagnostic ignored "-Wdocumentation"
10
+
11
+ #include "include/core/SkImage.h"
12
+
13
+ #pragma clang diagnostic pop
14
+
15
+ #include "RNSkPlatformContext.h"
16
+ #include "RNSkVideo.h"
17
+
18
+ namespace RNSkia {
19
+
20
+ class RNSkiOSVideo : public RNSkVideo {
21
+ private:
22
+ std::string _url;
23
+ AVAssetReader *_reader = nullptr;
24
+ AVAssetReaderTrackOutput *_trackOutput = nullptr;
25
+ RNSkPlatformContext *_context;
26
+ double _duration = 0;
27
+ double _framerate = 0;
28
+ void setupReader(CMTimeRange timeRange);
29
+ NSDictionary *getOutputSettings();
30
+
31
+ public:
32
+ RNSkiOSVideo(std::string url, RNSkPlatformContext *context);
33
+ ~RNSkiOSVideo();
34
+ sk_sp<SkImage> nextImage(double *timeStamp = nullptr) override;
35
+ double duration() override;
36
+ double framerate() override;
37
+ void seek(double timestamp) override;
38
+ };
39
+
40
+ } // namespace RNSkia