@shopify/react-native-skia 1.2.3 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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 +10 -0
  23. package/lib/commonjs/external/reanimated/useVideo.js +93 -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 +10 -0
  41. package/lib/module/external/reanimated/useVideo.js +85 -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 +10 -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 +115 -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