rays-video 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ea13fca90e2e0f9c175fad5283bb6f0cc8c763caf6386693151c9f8d477a1c1
4
- data.tar.gz: 4bfa9890019ed3d63c14272da0c4a1abbad784881c96b5b9078a60e4c34ee826
3
+ metadata.gz: 2bfe8646da57777c45c2061d2343f5872f095c5b05166abe3264b6c47150756b
4
+ data.tar.gz: b7216425b5f1941be61dfe3aa7744cedcfa72e21a95e552e466e28a3dfe623e8
5
5
  SHA512:
6
- metadata.gz: 3449d2b2ed3cc417529de8639d4be7438af541881b7e7e736ae79e096e8f92c4c0b235ab540f2759012a8adad9087533f6486c251f4feeedcfafc08cdcf36b4a
7
- data.tar.gz: 72bbfb7b9d3c7c84e41f89bc9d8cbf7bb70ca1c55b2438a296f56468de5752af0700c302715216ffad050aa73234317a51e70e26cc9a1dbbc0ab89cc42bf7743
6
+ metadata.gz: 120b483fdbe17c7890739cf20e2f6f143d0a47e962789aa76f49b8d118fe6f9f34f047a9370c0bd53d870060704c962606ae9c489ce6432cf1c8471483e183e9
7
+ data.tar.gz: 318a8c0ffb1c3f95e0dd1946863fbc6b768ddad518a1f9b3727c6f629f19174eb0ca47e724ed7a0fbb7deda6947644991aaf902a823785c4f09e4d16e958958c
@@ -9,7 +9,7 @@ RUCY_DEFINE_VALUE_FROM_TO(RAYS_VIDEO_EXPORT, Rays::Video)
9
9
 
10
10
  #define THIS to<Rays::Video*>(self)
11
11
 
12
- #define CHECK RUCY_CHECK_OBJECT(Rays::Video, self)
12
+ #define CHECK RUCY_CHECK_OBJ(Rays::Video, self)
13
13
 
14
14
 
15
15
  static
@@ -21,7 +21,7 @@ VALUE alloc(VALUE klass)
21
21
  static
22
22
  VALUE initialize(VALUE self, VALUE width, VALUE height, VALUE fps, VALUE pixel_density)
23
23
  {
24
- RUCY_CHECK_OBJ(Rays::Video, self);
24
+ RUCY_CHECK_PTR(Rays::Video, self);
25
25
 
26
26
  float fps_ = to<float>(fps);
27
27
  *THIS = Rays::Video(
@@ -34,32 +34,32 @@ VALUE initialize(VALUE self, VALUE width, VALUE height, VALUE fps, VALUE pixel_d
34
34
  static
35
35
  VALUE initialize_copy(VALUE self, VALUE obj)
36
36
  {
37
- RUCY_CHECK_OBJ(Rays::Video, self);
37
+ RUCY_CHECK_PTR(Rays::Video, self);
38
38
 
39
39
  *THIS = to<Rays::Video&>(obj).dup();
40
40
  }
41
41
 
42
42
  static
43
- VALUE insert(VALUE self, VALUE index, VALUE image)
43
+ VALUE play(VALUE self)
44
44
  {
45
45
  CHECK;
46
- THIS->insert(to<size_t>(index), to<const Rays::Image&>(image));
46
+ THIS->play();
47
47
  return self;
48
48
  }
49
49
 
50
50
  static
51
- VALUE append(VALUE self, VALUE image)
51
+ VALUE pause(VALUE self)
52
52
  {
53
53
  CHECK;
54
- THIS->append(to<const Rays::Image&>(image));
54
+ THIS->pause();
55
55
  return self;
56
56
  }
57
57
 
58
58
  static
59
- VALUE remove(VALUE self, VALUE index)
59
+ VALUE stop(VALUE self)
60
60
  {
61
61
  CHECK;
62
- THIS->remove(to<size_t>(index));
62
+ THIS->stop();
63
63
  return self;
64
64
  }
65
65
 
@@ -85,6 +85,13 @@ VALUE height(VALUE self)
85
85
  return value(THIS->height());
86
86
  }
87
87
 
88
+ static
89
+ VALUE pixel_density(VALUE self)
90
+ {
91
+ CHECK;
92
+ return value(THIS->pixel_density());
93
+ }
94
+
88
95
  static
89
96
  VALUE fps(VALUE self)
90
97
  {
@@ -93,102 +100,103 @@ VALUE fps(VALUE self)
93
100
  }
94
101
 
95
102
  static
96
- VALUE pixel_density(VALUE self)
103
+ VALUE set_position(VALUE self, VALUE position)
97
104
  {
98
105
  CHECK;
99
- return value(THIS->pixel_density());
106
+ THIS->set_position(to<size_t>(position));
107
+ return position;
100
108
  }
101
109
 
102
110
  static
103
- VALUE size(VALUE self)
111
+ VALUE get_position(VALUE self)
104
112
  {
105
113
  CHECK;
106
- return value(THIS->size());
114
+ return value(THIS->position());
107
115
  }
108
116
 
109
117
  static
110
- VALUE empty(VALUE self)
118
+ VALUE set_time_scale(VALUE self, VALUE scale)
111
119
  {
112
120
  CHECK;
113
- return value(THIS->empty());
121
+ THIS->set_time_scale(to<float>(scale));
122
+ return scale;
114
123
  }
115
124
 
116
125
  static
117
- VALUE set_position(VALUE self, VALUE position)
126
+ VALUE get_time_scale(VALUE self)
118
127
  {
119
128
  CHECK;
120
- THIS->set_position(to<size_t>(position));
121
- return position;
129
+ return value(THIS->time_scale());
122
130
  }
123
131
 
124
132
  static
125
- VALUE get_position(VALUE self)
133
+ VALUE insert(VALUE self, VALUE index, VALUE image)
126
134
  {
127
135
  CHECK;
128
- return value(THIS->position());
136
+ THIS->insert(to<size_t>(index), to<const Rays::Image&>(image));
137
+ return self;
129
138
  }
130
139
 
131
140
  static
132
- VALUE each(VALUE self)
141
+ VALUE append(VALUE self, VALUE image)
133
142
  {
134
143
  CHECK;
135
- Value ret;
136
- for (auto it = THIS->begin(), end = THIS->end(); it != end; ++it)
137
- ret = yield(value(*it));
138
- return ret;
144
+ THIS->append(to<const Rays::Image&>(image));
145
+ return self;
139
146
  }
140
147
 
141
148
  static
142
- VALUE to_image(VALUE self)
149
+ VALUE remove(VALUE self, VALUE index)
143
150
  {
144
151
  CHECK;
145
- return value((Rays::Image) *THIS);
152
+ THIS->remove(to<size_t>(index));
153
+ return self;
146
154
  }
147
155
 
148
156
  static
149
- VALUE at(VALUE self, VALUE index)
157
+ VALUE set_at(VALUE self, VALUE index, VALUE image)
150
158
  {
151
159
  CHECK;
152
- return value((*THIS)[(size_t) to<int>(index)]);
160
+ THIS->set(to<size_t>(index), to<const Rays::Image&>(image));
161
+ return image;
153
162
  }
154
163
 
155
164
  static
156
- VALUE play(VALUE self)
165
+ VALUE get_at(VALUE self, VALUE index)
157
166
  {
158
167
  CHECK;
159
- THIS->play();
160
- return self;
168
+ return value(THIS->get(to<size_t>(index)));
161
169
  }
162
170
 
163
171
  static
164
- VALUE pause(VALUE self)
172
+ VALUE size(VALUE self)
165
173
  {
166
174
  CHECK;
167
- THIS->pause();
168
- return self;
175
+ return value(THIS->size());
169
176
  }
170
177
 
171
178
  static
172
- VALUE stop(VALUE self)
179
+ VALUE empty(VALUE self)
173
180
  {
174
181
  CHECK;
175
- THIS->stop();
176
- return self;
182
+ return value(THIS->empty());
177
183
  }
178
184
 
179
185
  static
180
- VALUE set_time_scale(VALUE self, VALUE scale)
186
+ VALUE each(VALUE self)
181
187
  {
182
188
  CHECK;
183
- THIS->set_time_scale(to<float>(scale));
184
- return scale;
189
+ Value ret;
190
+ for (auto it = THIS->begin(), end = THIS->end(); it != end; ++it)
191
+ ret = yield(value(*it));
192
+ return ret;
185
193
  }
186
194
 
187
195
  static
188
- VALUE get_time_scale(VALUE self)
196
+ VALUE to_image(VALUE self)
189
197
  {
190
198
  CHECK;
191
- return value(THIS->time_scale());
199
+ return value((Rays::Image) *THIS);
192
200
  }
193
201
 
194
202
  static
@@ -200,10 +208,12 @@ VALUE load(VALUE self, VALUE path)
200
208
  static
201
209
  VALUE exts(VALUE self)
202
210
  {
203
- std::vector<Value> list;
204
- for (const auto& ext : Rays::get_video_exts())
205
- list.emplace_back(ext.c_str());
206
- return array(list.data(), list.size());
211
+ const auto& exts = Rays::get_video_exts();
212
+
213
+ Value list = array(NULL, exts.size());
214
+ for (const auto& ext : exts)
215
+ list.push(ext.c_str());
216
+ return list;
207
217
  }
208
218
 
209
219
 
@@ -218,26 +228,27 @@ Init_rays_video ()
218
228
  rb_define_alloc_func(cVideo, alloc);
219
229
  cVideo.define_private_method("initialize!", initialize);
220
230
  rb_define_private_method(cVideo, "initialize_copy", RUBY_METHOD_FUNC(initialize_copy), 1);
221
- cVideo.define_method("insert!", insert);
222
- cVideo.define_method("append!", append);
223
- cVideo.define_method("remove!", remove);
231
+ rb_define_method(cVideo, "play", RUBY_METHOD_FUNC(play), 0);
232
+ rb_define_method(cVideo, "pause", RUBY_METHOD_FUNC(pause), 0);
233
+ rb_define_method(cVideo, "stop", RUBY_METHOD_FUNC(stop), 0);
224
234
  rb_define_method(cVideo, "save", RUBY_METHOD_FUNC(save), 1);
225
235
  rb_define_method(cVideo, "width", RUBY_METHOD_FUNC(width), 0);
226
236
  rb_define_method(cVideo, "height", RUBY_METHOD_FUNC(height), 0);
227
- rb_define_method(cVideo, "fps", RUBY_METHOD_FUNC(fps), 0);
228
237
  rb_define_method(cVideo, "pixel_density", RUBY_METHOD_FUNC(pixel_density), 0);
229
- rb_define_method(cVideo, "size", RUBY_METHOD_FUNC(size), 0);
230
- cVideo.define_method("empty?", empty);
238
+ rb_define_method(cVideo, "fps", RUBY_METHOD_FUNC(fps), 0);
231
239
  rb_define_method(cVideo, "position=", RUBY_METHOD_FUNC(set_position), 1);
232
240
  rb_define_method(cVideo, "position", RUBY_METHOD_FUNC(get_position), 0);
233
- rb_define_method(cVideo, "play", RUBY_METHOD_FUNC(play), 0);
234
- rb_define_method(cVideo, "pause", RUBY_METHOD_FUNC(pause), 0);
235
- rb_define_method(cVideo, "stop", RUBY_METHOD_FUNC(stop), 0);
236
241
  rb_define_method(cVideo, "time_scale=", RUBY_METHOD_FUNC(set_time_scale), 1);
237
242
  rb_define_method(cVideo, "time_scale", RUBY_METHOD_FUNC(get_time_scale), 0);
243
+ cVideo.define_method("insert!", insert);
244
+ cVideo.define_method("append!", append);
245
+ cVideo.define_method("remove!", remove);
246
+ cVideo.define_method("[]=", set_at);
247
+ cVideo.define_method("[]", get_at);
248
+ rb_define_method(cVideo, "size", RUBY_METHOD_FUNC(size), 0);
249
+ cVideo.define_method("empty?", empty);
238
250
  cVideo.define_method("each!", each);
239
251
  rb_define_method(cVideo, "to_image", RUBY_METHOD_FUNC(to_image), 0);
240
- cVideo.define_method("[]", at);
241
252
  rb_define_module_function(cVideo, "load", RUBY_METHOD_FUNC(load), 1);
242
253
  rb_define_module_function(cVideo, "exts", RUBY_METHOD_FUNC(exts), 0);
243
254
  }
@@ -9,52 +9,5 @@ permissions:
9
9
 
10
10
  jobs:
11
11
  release:
12
- runs-on: macos-latest
13
-
14
- steps:
15
- - name: ruby 3.2
16
- uses: ruby/setup-ruby@v1
17
- with:
18
- ruby-version: 3.2
19
-
20
- - name: checkout
21
- uses: actions/checkout@v4
22
-
23
- - name: setup gems
24
- run: bundle install
25
-
26
- - name: setup dependencies
27
- run: "ruby -I.github/workflows -rutils -e 'setup_dependencies'"
28
-
29
- - name: packages
30
- run: bundle exec rake packages
31
-
32
- - name: setup zig
33
- if: github.event.repository.name == 'reflex-terminal'
34
- uses: mlugg/setup-zig@v2
35
- with:
36
- version: 0.16.0
37
-
38
- - name: test
39
- run: bundle exec rake test
40
-
41
- - name: create gem
42
- id: gem
43
- run: |
44
- bundle exec rake gem
45
- echo path=$(ruby -e 'print Dir.glob("*.gem").first') >> $GITHUB_OUTPUT
46
-
47
- - name: create github release
48
- env:
49
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50
- run: ruby -I.github/workflows -rutils -e 'release(*ARGV)' ./${{ steps.gem.outputs.path }}
51
-
52
- - name: upload to rubygems
53
- env:
54
- GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
55
- run: |
56
- mkdir -p $HOME/.gem
57
- touch $HOME/.gem/credentials
58
- chmod 0600 $HOME/.gem/credentials
59
- printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
60
- bundle exec rake upload
12
+ uses: xord/xot/.github/workflows/release-gem.yml@master
13
+ secrets: inherit
@@ -1,35 +1,12 @@
1
1
  name: Tag
2
2
 
3
3
  on:
4
- push:
4
+ workflow_run:
5
+ workflows: [Test]
6
+ types: [completed]
5
7
  branches: [master]
6
8
 
7
9
  jobs:
8
10
  tag:
9
- runs-on: ubuntu-latest
10
-
11
- steps:
12
- - name: ruby 3.2
13
- uses: ruby/setup-ruby@v1
14
- with:
15
- ruby-version: 3.2
16
-
17
- - name: checkout
18
- uses: actions/checkout@v4
19
- with:
20
- fetch-depth: 0
21
- token: ${{ secrets.PAT }}
22
-
23
- - name: setup dependencies
24
- run: "ruby -I.github/workflows -rutils -e 'setup_dependencies only: :xot'"
25
-
26
- - name: setup user name and email
27
- run: |
28
- git config --global user.email "xordog@gmail.com"
29
- git config --global user.name "xord"
30
-
31
- - name: tag versions
32
- run: "ruby -I.github/workflows -rutils -e 'tag_versions'"
33
-
34
- - name: push tags
35
- run: git push origin --tags
11
+ uses: xord/xot/.github/workflows/tag.yml@master
12
+ secrets: inherit
@@ -7,40 +7,4 @@ on:
7
7
 
8
8
  jobs:
9
9
  test:
10
- runs-on: macos-latest
11
-
12
- steps:
13
- - name: ruby 3.2
14
- uses: ruby/setup-ruby@v1
15
- with:
16
- ruby-version: 3.2
17
-
18
- - name: checkout
19
- uses: actions/checkout@v4
20
-
21
- - name: setup gems
22
- run: bundle install
23
-
24
- - name: setup dependencies
25
- run: "ruby -I.github/workflows -rutils -e 'setup_dependencies'"
26
-
27
- - name: packages
28
- run: bundle exec rake verbose packages
29
-
30
- - name: setup zig
31
- if: github.event.repository.name == 'reflex-terminal'
32
- uses: mlugg/setup-zig@v2
33
- with:
34
- version: 0.16.0
35
-
36
- - name: vendor
37
- run: bundle exec rake vendor
38
-
39
- - name: lib
40
- run: bundle exec rake verbose lib
41
-
42
- - name: ext
43
- run: bundle exec rake verbose ext
44
-
45
- - name: test
46
- run: bundle exec rake verbose test
10
+ uses: xord/xot/.github/workflows/test.yml@master
data/ChangeLog.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # rays-video ChangeLog
2
2
 
3
3
 
4
+ ## [v0.3.0] - 2026-11-04
5
+
6
+ - [BREAKING] Make the frames of a video read-only views sharing one decoded frame, and take a writable copy by Image#dup
7
+ - [BREAKING] Reject frames whose size or pixel density differs from the video's
8
+ - Decode video files sequentially with AVAssetReader, which is many times faster
9
+ - Add Video#get and #set, as #[] and #[]=
10
+ - Add Video#push and #<< as aliases of #append, which returns self now
11
+ - Show the last frame for an index past the estimated last one
12
+
13
+ - Fix Video#insert crashing on an index past the end, and raise IndexError
14
+ - Fix a crash on the error paths, as saving a GIF to a path that cannot be written
15
+ - Fix linking the extension statically on macOS
16
+ - Fix the build for iOS
17
+
18
+
4
19
  ## [v0.2.0] - 2026-08-04
5
20
 
6
21
  - Update dependencies
data/Gemfile.lock ADDED
@@ -0,0 +1,26 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ power_assert (3.1.0)
5
+ rake (13.4.2)
6
+ test-unit (3.7.8)
7
+ power_assert
8
+ yard (0.9.45)
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ rake
15
+ test-unit
16
+ yard
17
+
18
+ CHECKSUMS
19
+ bundler (4.0.20) sha256=7978a8ac648767f5e635bc522445b79e80a52b907a39a36c2d8085ed6bc762ae
20
+ power_assert (3.1.0) sha256=fefe98ed5d3140628138f3f9f9194d6ece01f1320e69700d3c928adca7518251
21
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
22
+ test-unit (3.7.8) sha256=689d1ca53f4d46f678b4e5d68d8e4294f87fc5e8b9238cc4de7c5727e8d65943
23
+ yard (0.9.45) sha256=52e211493f7cb8a3ebf7e104a25a1e73937a3103092545d34cb88fafebb3dc51
24
+
25
+ BUNDLED WITH
26
+ 4.0.20
data/README.md CHANGED
@@ -64,20 +64,28 @@ A finite sequence of `Rays::Image` frames with a fixed `width`, `height`, `fps`,
64
64
  | `Video.new(width, height, fps:, pixel_density:)` | Create an empty video (`fps` defaults to 30) |
65
65
  | `Video.load(path)` | Load a video file from disk |
66
66
  | `Video.exts` | Supported video file extensions on the current platform |
67
- | `video.append(*images)` | Append one or more frames |
67
+ | `video.append(*images)` | Append one or more frames (also `push` and `<<`) |
68
68
  | `video.insert(index, *images)` | Insert frames at the given index |
69
69
  | `video.remove(index)` | Remove the frame at the given index |
70
70
  | `video.each { \|image\| ... }` | Iterate frames (also includes `Enumerable`) |
71
71
  | `video[i]` | Get the frame at index *i* as a `Rays::Image` |
72
+ | `video[i] = image` | Replace the frame at index *i* |
72
73
  | `video.pos` / `video.pos =` | Current playback position (index) |
73
74
  | `video.play` / `video.pause` / `video.stop`| Playback controls |
74
75
  | `video.time_scale` / `video.time_scale =` | Speed multiplier for playback |
75
76
  | `video.size`, `video.empty?` | Frame count / emptiness |
76
77
  | `video.width`, `video.height`, `video.fps`, `video.pixel_density` | Read-only metadata |
77
- | `video.dup` | Deep-ish copy (shares image references) |
78
+ | `video.dup` | Copy the frame list; loaded frames get their own decoder |
78
79
  | `video.save(path)` | Encode the video to a file |
79
80
  | `video.to_image` (`Image()` cast) | Get the frame at the current `pos` as a `Rays::Image` |
80
81
 
82
+ Every frame must have the video's `width`, `height` and `pixel_density`.
83
+
84
+ Frames of a loaded video are read-only views: each source keeps a single
85
+ decoded frame, and a frame's pixels are decoded into it on access. Such
86
+ frames are frozen, so `paint` and `[]=` raise `FrozenError`. `dup` a frame
87
+ to keep or edit it, and put it back with `video[i] = image`.
88
+
81
89
  ## 💡 Usage
82
90
 
83
91
  ### Build a video from frames and save it
@@ -129,6 +137,20 @@ video.pos = video.size / 2 # mid-point
129
137
  mid = video.to_image
130
138
  ```
131
139
 
140
+ ### Edit a frame of a loaded video
141
+
142
+ ```ruby
143
+ video = Rays::Video.load 'clip.mp4'
144
+
145
+ frame = video[30].dup # loaded frames are frozen, so copy first
146
+ frame.paint do |p|
147
+ p.fill 1, 0, 0
148
+ p.ellipse 10, 10, 50, 50
149
+ end
150
+ video[30] = frame
151
+ video.save 'edited.mp4'
152
+ ```
153
+
132
154
  ## 🛠️ Development
133
155
 
134
156
  ```bash
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -15,7 +15,8 @@ Xot::ExtConf.new Xot, Rucy, Beeps, Rays, RaysVideo do
15
15
  setup do
16
16
  headers << 'ruby.h'
17
17
  libs.unshift 'gdi32', 'opengl32', 'glew32' if win32?
18
- frameworks << 'AppKit' << 'AVFoundation' if osx?
18
+ frameworks.concat %w[
19
+ AppKit AVFoundation UniformTypeIdentifiers] if osx?
19
20
  $LDFLAGS << ' -Wl,--out-implib=librays-video.dll.a' if mingw? || cygwin?
20
21
  end
21
22