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 +4 -4
- data/.doc/ext/rays-video/video.cpp +68 -57
- data/.github/workflows/release-gem.yml +2 -49
- data/.github/workflows/tag.yml +5 -28
- data/.github/workflows/test.yml +1 -37
- data/ChangeLog.md +15 -0
- data/Gemfile.lock +26 -0
- data/README.md +24 -2
- data/VERSION +1 -1
- data/ext/rays-video/extconf.rb +2 -1
- data/ext/rays-video/video.cpp +72 -60
- data/include/rays/video.h +19 -15
- data/lib/rays/video.rb +4 -0
- data/rays-video.gemspec +5 -5
- data/src/ios/video.mm +1 -632
- data/src/ios/video_audio_in.mm +1 -251
- data/src/osx/video.mm +283 -57
- data/src/osx/video_audio_in.mm +2 -1
- data/src/sdl/video.cpp +19 -13
- data/src/video.cpp +202 -85
- data/src/video.h +7 -5
- data/src/win32/video.cpp +19 -13
- data/test/test_video.rb +164 -27
- metadata +12 -13
- data/.github/workflows/utils.rb +0 -127
- data/src/ios/video_audio_in.h +0 -22
data/src/video.cpp
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#include "video.h"
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
#include <
|
|
4
|
+
#include <cmath>
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <map>
|
|
5
7
|
#include <beeps/sound.h>
|
|
6
8
|
#include "rays/bitmap.h"
|
|
7
9
|
#include "rays/exception.h"
|
|
@@ -11,6 +13,64 @@ namespace Rays
|
|
|
11
13
|
{
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
struct VideoReader
|
|
17
|
+
{
|
|
18
|
+
|
|
19
|
+
typedef std::shared_ptr<VideoReader> Ptr;
|
|
20
|
+
|
|
21
|
+
typedef std:: weak_ptr<VideoReader> WeakPtr;
|
|
22
|
+
|
|
23
|
+
VideoDecoder decoder;
|
|
24
|
+
|
|
25
|
+
Image pixels;
|
|
26
|
+
|
|
27
|
+
ssize_t loaded_index = -1;
|
|
28
|
+
|
|
29
|
+
VideoReader (const VideoDecoder& decoder, float pixel_density)
|
|
30
|
+
: decoder(decoder),
|
|
31
|
+
pixels(
|
|
32
|
+
(int) (decoder.width() / pixel_density),
|
|
33
|
+
(int) (decoder.height() / pixel_density),
|
|
34
|
+
RGBA, pixel_density)
|
|
35
|
+
{
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
};// VideoReader
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
struct FrameLoader : public Image::Loader
|
|
42
|
+
{
|
|
43
|
+
|
|
44
|
+
VideoReader::Ptr reader;
|
|
45
|
+
|
|
46
|
+
ssize_t index;
|
|
47
|
+
|
|
48
|
+
FrameLoader (const VideoReader::Ptr& reader, ssize_t index)
|
|
49
|
+
: reader(reader), index(index)
|
|
50
|
+
{
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
bool load (Bitmap* bitmap) override
|
|
54
|
+
{
|
|
55
|
+
VideoReader* r = reader.get();
|
|
56
|
+
if (r->loaded_index == index)
|
|
57
|
+
return false;
|
|
58
|
+
|
|
59
|
+
r->decoder.get_bitmap(bitmap, index);
|
|
60
|
+
r->loaded_index = index;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
};// FrameLoader
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
static Image
|
|
68
|
+
make_frame (const VideoReader::Ptr& reader, ssize_t index)
|
|
69
|
+
{
|
|
70
|
+
return Image(reader->pixels, new FrameLoader(reader, index));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
14
74
|
struct Video::Data
|
|
15
75
|
{
|
|
16
76
|
|
|
@@ -22,57 +82,98 @@ namespace Rays
|
|
|
22
82
|
|
|
23
83
|
std::vector<Image> images;
|
|
24
84
|
|
|
85
|
+
std::map<String, VideoReader::WeakPtr> readers;
|
|
86
|
+
|
|
25
87
|
VideoAudioInList audio_tracks;
|
|
26
88
|
|
|
27
89
|
Beeps::SoundPlayer player;
|
|
28
90
|
|
|
29
|
-
|
|
91
|
+
VideoReader::Ptr get_reader (const VideoDecoder& decoder)
|
|
92
|
+
{
|
|
93
|
+
if (!decoder)
|
|
94
|
+
argument_error(__FILE__, __LINE__, "invalid decoder");
|
|
95
|
+
|
|
96
|
+
VideoReader::WeakPtr& weak = readers[decoder.path()];
|
|
97
|
+
VideoReader::Ptr reader = weak.lock();
|
|
98
|
+
if (!reader)
|
|
99
|
+
{
|
|
100
|
+
reader.reset(new VideoReader(VideoDecoder(decoder.path()), pixel_density));
|
|
101
|
+
weak = reader;
|
|
102
|
+
}
|
|
103
|
+
return reader;
|
|
104
|
+
}
|
|
30
105
|
|
|
106
|
+
Image to_frame (const Image& image)
|
|
107
|
+
{
|
|
108
|
+
auto* loader = dynamic_cast<const FrameLoader*>(image.loader());
|
|
109
|
+
if (!loader) return image;
|
|
31
110
|
|
|
32
|
-
|
|
33
|
-
|
|
111
|
+
return make_frame(get_reader(loader->reader->decoder), loader->index);
|
|
112
|
+
}
|
|
34
113
|
|
|
35
|
-
|
|
114
|
+
};// Video::Data
|
|
36
115
|
|
|
37
|
-
size_t index;
|
|
38
116
|
|
|
39
|
-
|
|
40
|
-
|
|
117
|
+
static void
|
|
118
|
+
check_index (const Video& video, size_t index)
|
|
119
|
+
{
|
|
120
|
+
if (video.empty())
|
|
41
121
|
{
|
|
122
|
+
index_error(
|
|
123
|
+
__FILE__, __LINE__, "index %zu is out of range (empty)", index);
|
|
42
124
|
}
|
|
43
|
-
|
|
44
|
-
void preprocess (const Image* image) const override
|
|
125
|
+
if (index >= video.size())
|
|
45
126
|
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (bitmap) Xot::hint_memory_usage(bitmap.size());
|
|
49
|
-
const_cast<Image*>(image)->self = decoded.self;
|
|
127
|
+
index_error(
|
|
128
|
+
__FILE__, __LINE__, "index %zu is out of range (0..%zu)", index, video.size() - 1);
|
|
50
129
|
}
|
|
130
|
+
}
|
|
51
131
|
|
|
52
|
-
|
|
53
|
-
|
|
132
|
+
static void
|
|
133
|
+
check_frame (const Video& video, const Image& image)
|
|
134
|
+
{
|
|
135
|
+
if (!image)
|
|
136
|
+
argument_error(__FILE__, __LINE__, "image is empty");
|
|
137
|
+
|
|
138
|
+
long w = std::lround(image.width());
|
|
139
|
+
long h = std::lround(image.height());
|
|
140
|
+
long pw = std::lround(image.width() * image.pixel_density());
|
|
141
|
+
long ph = std::lround(image.height() * image.pixel_density());
|
|
142
|
+
long vw = std::lround(video.width());
|
|
143
|
+
long vh = std::lround(video.height());
|
|
144
|
+
long vpw = std::lround(video.width() * video.pixel_density());
|
|
145
|
+
long vph = std::lround(video.height() * video.pixel_density());
|
|
146
|
+
if (w != vw || h != vh || pw != vpw || ph != vph)
|
|
147
|
+
{
|
|
148
|
+
argument_error(
|
|
149
|
+
__FILE__, __LINE__,
|
|
150
|
+
"frame size %ldx%ld (%ldx%ld px) does not match the video size %ldx%ld (%ldx%ld px)",
|
|
151
|
+
w, h, pw, ph, vw, vh, vpw, vph);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
54
154
|
|
|
55
155
|
Video
|
|
56
156
|
load_video (const char* path)
|
|
57
157
|
{
|
|
58
|
-
|
|
59
|
-
if (!
|
|
158
|
+
VideoDecoder decoder(path);
|
|
159
|
+
if (!decoder)
|
|
60
160
|
invalid_state_error(__FILE__, __LINE__);
|
|
61
161
|
|
|
62
162
|
Video video;
|
|
63
163
|
Video::Data* self = video.self.get();
|
|
64
|
-
self->width =
|
|
65
|
-
self->height =
|
|
66
|
-
self->fps =
|
|
164
|
+
self->width = decoder.width();
|
|
165
|
+
self->height = decoder.height();
|
|
166
|
+
self->fps = decoder.fps();
|
|
67
167
|
self->pixel_density = 1;
|
|
68
168
|
self->position = 0;
|
|
69
169
|
|
|
70
|
-
|
|
170
|
+
VideoReader::Ptr reader = self->get_reader(decoder);
|
|
171
|
+
size_t size = decoder.size();
|
|
71
172
|
self->images.reserve(size);
|
|
72
173
|
for (size_t i = 0; i < size; ++i)
|
|
73
|
-
self->images.push_back(
|
|
174
|
+
self->images.push_back(make_frame(reader, i));
|
|
74
175
|
|
|
75
|
-
self->audio_tracks =
|
|
176
|
+
self->audio_tracks = decoder.get_audio_tracks();
|
|
76
177
|
|
|
77
178
|
return video;
|
|
78
179
|
}
|
|
@@ -108,29 +209,12 @@ namespace Rays
|
|
|
108
209
|
{
|
|
109
210
|
Video v;
|
|
110
211
|
*v.self = *self;
|
|
111
|
-
return v;
|
|
112
|
-
}
|
|
113
212
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (!*this)
|
|
118
|
-
invalid_state_error(__FILE__, __LINE__, "video is not initialized");
|
|
119
|
-
|
|
120
|
-
self->images.insert(self->images.begin() + index, image);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
void
|
|
124
|
-
Video::append (const Image& image)
|
|
125
|
-
{
|
|
126
|
-
insert(size(), image);
|
|
127
|
-
}
|
|
213
|
+
v.self->readers.clear();
|
|
214
|
+
for (auto& image : v.self->images)
|
|
215
|
+
image = v.self->to_frame(image);
|
|
128
216
|
|
|
129
|
-
|
|
130
|
-
Video::remove (size_t index)
|
|
131
|
-
{
|
|
132
|
-
if (index >= size()) return;
|
|
133
|
-
self->images.erase(self->images.begin() + index);
|
|
217
|
+
return v;
|
|
134
218
|
}
|
|
135
219
|
|
|
136
220
|
void
|
|
@@ -158,18 +242,6 @@ namespace Rays
|
|
|
158
242
|
if (self->player) self->player.stop();
|
|
159
243
|
}
|
|
160
244
|
|
|
161
|
-
void
|
|
162
|
-
Video::set_time_scale (float scale)
|
|
163
|
-
{
|
|
164
|
-
if (self->player) self->player.set_time_scale(scale);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
float
|
|
168
|
-
Video::time_scale () const
|
|
169
|
-
{
|
|
170
|
-
return self->player ? self->player.time_scale() : 1;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
245
|
coord
|
|
174
246
|
Video::width () const
|
|
175
247
|
{
|
|
@@ -182,28 +254,16 @@ namespace Rays
|
|
|
182
254
|
return self->height;
|
|
183
255
|
}
|
|
184
256
|
|
|
185
|
-
float
|
|
186
|
-
Video::fps () const
|
|
187
|
-
{
|
|
188
|
-
return self->fps;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
257
|
float
|
|
192
258
|
Video::pixel_density () const
|
|
193
259
|
{
|
|
194
260
|
return self->pixel_density;
|
|
195
261
|
}
|
|
196
262
|
|
|
197
|
-
|
|
198
|
-
Video::
|
|
199
|
-
{
|
|
200
|
-
return self->images.size();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
bool
|
|
204
|
-
Video::empty () const
|
|
263
|
+
float
|
|
264
|
+
Video::fps () const
|
|
205
265
|
{
|
|
206
|
-
return self->
|
|
266
|
+
return self->fps;
|
|
207
267
|
}
|
|
208
268
|
|
|
209
269
|
void
|
|
@@ -222,6 +282,75 @@ namespace Rays
|
|
|
222
282
|
return self->position;
|
|
223
283
|
}
|
|
224
284
|
|
|
285
|
+
void
|
|
286
|
+
Video::set_time_scale (float scale)
|
|
287
|
+
{
|
|
288
|
+
if (self->player) self->player.set_time_scale(scale);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
float
|
|
292
|
+
Video::time_scale () const
|
|
293
|
+
{
|
|
294
|
+
return self->player ? self->player.time_scale() : 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
void
|
|
298
|
+
Video::insert (size_t index, const Image& image)
|
|
299
|
+
{
|
|
300
|
+
if (!*this)
|
|
301
|
+
invalid_state_error(__FILE__, __LINE__, "video is not initialized");
|
|
302
|
+
if (index > size())
|
|
303
|
+
{
|
|
304
|
+
index_error(
|
|
305
|
+
__FILE__, __LINE__, "index %zu is out of range (0..%zu)", index, size());
|
|
306
|
+
}
|
|
307
|
+
check_frame(*this, image);
|
|
308
|
+
|
|
309
|
+
self->images.insert(self->images.begin() + index, self->to_frame(image));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
void
|
|
313
|
+
Video::append (const Image& image)
|
|
314
|
+
{
|
|
315
|
+
insert(size(), image);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void
|
|
319
|
+
Video::remove (size_t index)
|
|
320
|
+
{
|
|
321
|
+
if (index >= size()) return;
|
|
322
|
+
self->images.erase(self->images.begin() + index);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void
|
|
326
|
+
Video::set (size_t index, const Image& image)
|
|
327
|
+
{
|
|
328
|
+
check_index(*this, index);
|
|
329
|
+
check_frame(*this, image);
|
|
330
|
+
|
|
331
|
+
self->images[index] = self->to_frame(image);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
Image
|
|
335
|
+
Video::get (size_t index) const
|
|
336
|
+
{
|
|
337
|
+
check_index(*this, index);
|
|
338
|
+
|
|
339
|
+
return self->images[index];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
size_t
|
|
343
|
+
Video::size () const
|
|
344
|
+
{
|
|
345
|
+
return self->images.size();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
bool
|
|
349
|
+
Video::empty () const
|
|
350
|
+
{
|
|
351
|
+
return self->images.empty();
|
|
352
|
+
}
|
|
353
|
+
|
|
225
354
|
Video::const_iterator
|
|
226
355
|
Video::begin () const
|
|
227
356
|
{
|
|
@@ -237,19 +366,7 @@ namespace Rays
|
|
|
237
366
|
Image
|
|
238
367
|
Video::operator [] (size_t index) const
|
|
239
368
|
{
|
|
240
|
-
|
|
241
|
-
{
|
|
242
|
-
index_error(
|
|
243
|
-
__FILE__, __LINE__, "index %zu is out of range (empty)",
|
|
244
|
-
index);
|
|
245
|
-
}
|
|
246
|
-
if (index >= size())
|
|
247
|
-
{
|
|
248
|
-
index_error(
|
|
249
|
-
__FILE__, __LINE__, "index %zu is out of range (0..%zu)",
|
|
250
|
-
index, size() - 1);
|
|
251
|
-
}
|
|
252
|
-
return self->images[index];
|
|
369
|
+
return get(index);
|
|
253
370
|
}
|
|
254
371
|
|
|
255
372
|
Video::operator Image () const
|
data/src/video.h
CHANGED
|
@@ -12,19 +12,21 @@ namespace Rays
|
|
|
12
12
|
{
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class VideoDecoder
|
|
16
16
|
{
|
|
17
17
|
|
|
18
18
|
public:
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
VideoDecoder ();
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
VideoDecoder (const char* path);
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
void get_bitmap (Bitmap* bitmap, size_t index);
|
|
25
25
|
|
|
26
26
|
VideoAudioInList get_audio_tracks () const;
|
|
27
27
|
|
|
28
|
+
const char* path () const;
|
|
29
|
+
|
|
28
30
|
coord width () const;
|
|
29
31
|
|
|
30
32
|
coord height () const;
|
|
@@ -41,7 +43,7 @@ namespace Rays
|
|
|
41
43
|
|
|
42
44
|
Xot::PSharedImpl<Data> self;
|
|
43
45
|
|
|
44
|
-
};//
|
|
46
|
+
};// VideoDecoder
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
}// Rays
|
data/src/win32/video.cpp
CHANGED
|
@@ -8,63 +8,69 @@ namespace Rays
|
|
|
8
8
|
{
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
struct
|
|
11
|
+
struct VideoDecoder::Data
|
|
12
12
|
{
|
|
13
|
-
};//
|
|
13
|
+
};// VideoDecoder::Data
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
VideoDecoder::VideoDecoder ()
|
|
17
17
|
{
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
VideoDecoder::VideoDecoder (const char*)
|
|
21
21
|
{
|
|
22
22
|
not_implemented_error(__FILE__, __LINE__);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
void
|
|
26
|
+
VideoDecoder::get_bitmap (Bitmap*, size_t)
|
|
27
27
|
{
|
|
28
28
|
not_implemented_error(__FILE__, __LINE__);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
VideoAudioInList
|
|
32
|
-
|
|
32
|
+
VideoDecoder::get_audio_tracks () const
|
|
33
33
|
{
|
|
34
34
|
return {};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const char*
|
|
38
|
+
VideoDecoder::path () const
|
|
39
|
+
{
|
|
40
|
+
not_implemented_error(__FILE__, __LINE__);
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
coord
|
|
38
|
-
|
|
44
|
+
VideoDecoder::width () const
|
|
39
45
|
{
|
|
40
46
|
return 0;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
coord
|
|
44
|
-
|
|
50
|
+
VideoDecoder::height () const
|
|
45
51
|
{
|
|
46
52
|
return 0;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
float
|
|
50
|
-
|
|
56
|
+
VideoDecoder::fps () const
|
|
51
57
|
{
|
|
52
58
|
return 0;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
size_t
|
|
56
|
-
|
|
62
|
+
VideoDecoder::size () const
|
|
57
63
|
{
|
|
58
64
|
return 0;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
VideoDecoder::operator bool () const
|
|
62
68
|
{
|
|
63
69
|
return false;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
bool
|
|
67
|
-
|
|
73
|
+
VideoDecoder::operator ! () const
|
|
68
74
|
{
|
|
69
75
|
return !operator bool();
|
|
70
76
|
}
|