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.
data/src/video.cpp CHANGED
@@ -1,7 +1,9 @@
1
1
  #include "video.h"
2
2
 
3
3
 
4
- #include <xot/util.h>
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
- };// Video::Data
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
- struct VideoImageData : public Image::Data
33
- {
111
+ return make_frame(get_reader(loader->reader->decoder), loader->index);
112
+ }
34
113
 
35
- VideoReader reader;
114
+ };// Video::Data
36
115
 
37
- size_t index;
38
116
 
39
- VideoImageData (const VideoReader& reader, size_t index)
40
- : reader(reader), index(index)
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
- Image decoded = reader.decode_image(index, 1);
47
- Bitmap bitmap = decoded.bitmap();
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
- };// VideoImageData
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
- VideoReader reader(path);
59
- if (!reader)
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 = reader.width();
65
- self->height = reader.height();
66
- self->fps = reader.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
- size_t size = reader.size();
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(Image(new VideoImageData(reader, i)));
174
+ self->images.push_back(make_frame(reader, i));
74
175
 
75
- self->audio_tracks = reader.get_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
- void
115
- Video::insert (size_t index, const Image& image)
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
- void
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
- size_t
198
- Video::size () const
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->images.empty();
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
- if (empty())
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 VideoReader
15
+ class VideoDecoder
16
16
  {
17
17
 
18
18
  public:
19
19
 
20
- VideoReader ();
20
+ VideoDecoder ();
21
21
 
22
- VideoReader (const char* path);
22
+ VideoDecoder (const char* path);
23
23
 
24
- Image decode_image (size_t index, float pixel_density) const;
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
- };// VideoReader
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 VideoReader::Data
11
+ struct VideoDecoder::Data
12
12
  {
13
- };// VideoReader::Data
13
+ };// VideoDecoder::Data
14
14
 
15
15
 
16
- VideoReader::VideoReader ()
16
+ VideoDecoder::VideoDecoder ()
17
17
  {
18
18
  }
19
19
 
20
- VideoReader::VideoReader (const char*)
20
+ VideoDecoder::VideoDecoder (const char*)
21
21
  {
22
22
  not_implemented_error(__FILE__, __LINE__);
23
23
  }
24
24
 
25
- Image
26
- VideoReader::decode_image (size_t, float) const
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
- VideoReader::get_audio_tracks () const
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
- VideoReader::width () const
44
+ VideoDecoder::width () const
39
45
  {
40
46
  return 0;
41
47
  }
42
48
 
43
49
  coord
44
- VideoReader::height () const
50
+ VideoDecoder::height () const
45
51
  {
46
52
  return 0;
47
53
  }
48
54
 
49
55
  float
50
- VideoReader::fps () const
56
+ VideoDecoder::fps () const
51
57
  {
52
58
  return 0;
53
59
  }
54
60
 
55
61
  size_t
56
- VideoReader::size () const
62
+ VideoDecoder::size () const
57
63
  {
58
64
  return 0;
59
65
  }
60
66
 
61
- VideoReader::operator bool () const
67
+ VideoDecoder::operator bool () const
62
68
  {
63
69
  return false;
64
70
  }
65
71
 
66
72
  bool
67
- VideoReader::operator ! () const
73
+ VideoDecoder::operator ! () const
68
74
  {
69
75
  return !operator bool();
70
76
  }