yta-video-opengl 0.0.19__py3-none-any.whl → 0.0.21__py3-none-any.whl

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.
@@ -1,562 +0,0 @@
1
- from yta_video_opengl.complete.video_on_track import VideoOnTrack
2
- from yta_video_opengl.video import Video
3
- from yta_video_opengl.t import T
4
- from yta_video_opengl.complete.frame_wrapper import VideoFrameWrapped
5
- from yta_video_opengl.utils import audio_frames_and_remainder_per_video_frame
6
- from yta_video_opengl.t import fps_to_time_base
7
- from yta_video_opengl.complete.frame_wrapper import AudioFrameWrapped
8
- from yta_video_opengl.complete.frame_generator import VideoFrameGenerator, AudioFrameGenerator
9
- from yta_validation.parameter import ParameterValidator
10
- from quicktions import Fraction
11
- from typing import Union
12
-
13
-
14
- NON_LIMITED_EMPTY_PART_END = 999
15
- """
16
- A value to indicate that the empty part
17
- has no end because it is in the last
18
- position and there is no video after it.
19
- """
20
- class _Part:
21
- """
22
- Class to represent an element that is on the
23
- track, that can be an empty space or a video
24
- (with audio).
25
- """
26
-
27
- @property
28
- def is_empty_part(
29
- self
30
- ) -> bool:
31
- """
32
- Flag to indicate if the part is an empty part,
33
- which means that there is no video associated
34
- but an empty space.
35
- """
36
- return self.video is None
37
-
38
- def __init__(
39
- self,
40
- track: 'Track',
41
- start: Union[int, float, Fraction],
42
- end: Union[int, float, Fraction],
43
- video: Union[VideoOnTrack, None] = None
44
- ):
45
- ParameterValidator.validate_mandatory_positive_number('start', start, do_include_zero = True)
46
- ParameterValidator.validate_mandatory_positive_number('end', end, do_include_zero = False)
47
- ParameterValidator.validate_instance_of('video', video, VideoOnTrack)
48
-
49
- self._track: Track = track
50
- """
51
- The instance of the track this part belongs
52
- to.
53
- """
54
- # TODO: I would like to avoid this 2 instances
55
- # here, and I think I've done it with static
56
- # properties in other project, but as I don't
57
- # remember how and where by now, here it is...
58
- self._video_frame_generator: VideoFrameGenerator = VideoFrameGenerator()
59
- """
60
- Useful internal tool to generate background
61
- frames for the empty parts.
62
- """
63
- self._audio_frame_generator: AudioFrameGenerator = AudioFrameGenerator()
64
- """
65
- Useful internal tool to generate silent
66
- audio frames for the empty parts.
67
- """
68
- self.start: Fraction = Fraction(start)
69
- """
70
- The start 't' time moment of the part.
71
- """
72
- self.end: Fraction = Fraction(end)
73
- """
74
- The end 't' time moment of the part.
75
- """
76
- self.video: Union[VideoOnTrack, None] = video
77
- """
78
- The video associated, if existing, or
79
- None if it is an empty space that we need
80
- to fulfill with a black background and
81
- silent audio.
82
- """
83
-
84
- def get_frame_at(
85
- self,
86
- t: Union[int, float, Fraction]
87
- ) -> 'VideoFrameWrapped':
88
- """
89
- Get the frame that must be displayed at
90
- the given 't' time moment.
91
- """
92
- frame = (
93
- # TODO: What about the 'format' (?)
94
- # TODO: Maybe I shouldn't set the 'time_base'
95
- # here and do it just in the Timeline 'render'
96
- #return get_black_background_video_frame(self._track.size)
97
- # TODO: This 'time_base' maybe has to be related
98
- # to a Timeline general 'time_base' and not the fps
99
- VideoFrameWrapped(
100
- frame = self._video_frame_generator.background.full_black(
101
- size = self._track.size,
102
- time_base = fps_to_time_base(self._track.fps)
103
- ),
104
- is_from_empty_part = True
105
- )
106
- if self.is_empty_part else
107
- VideoFrameWrapped(
108
- frame = self.video.get_frame_at(t),
109
- is_from_empty_part = False
110
- )
111
-
112
- )
113
-
114
- # TODO: This should not happen because of
115
- # the way we handle the videos here but the
116
- # video could send us a None frame here, so
117
- # do we raise exception (?)
118
- if frame._frame is None:
119
- #frame = get_black_background_video_frame(self._track.size)
120
- # TODO: By now I'm raising exception to check if
121
- # this happens or not because I think it would
122
- # be malfunctioning
123
- raise Exception(f'Video is returning None video frame at t={str(t)}.')
124
-
125
- return frame
126
-
127
- def get_audio_frames_at(
128
- self,
129
- t: Union[int, float, Fraction]
130
- ):
131
- """
132
- Iterate over all the audio frames that
133
- exist at the time moment 't' provided.
134
- """
135
- if not self.is_empty_part:
136
- for frame in self.video.get_audio_frames_at(t):
137
- yield AudioFrameWrapped(
138
- frame = frame,
139
- is_from_empty_part = False
140
- )
141
- else:
142
- frames = generate_silent_frames(
143
- fps = self._track.fps,
144
- audio_fps = self._track.audio_fps,
145
- audio_samples_per_frame = self._track.audio_samples_per_frame,
146
- # TODO: Where do this 2 formats come from (?)
147
- layout = self._track.audio_layout,
148
- format = self._track.audio_format
149
- )
150
-
151
- for frame in frames:
152
- yield frame
153
-
154
-
155
- # TODO: I don't like using t as float,
156
- # we need to implement fractions.Fraction
157
- # TODO: This is called Track but it is
158
- # handling videos only. Should I have
159
- # VideoTrack and AudioTrack (?)
160
- class Track:
161
- """
162
- Class to represent a track in which we place
163
- videos, images and audio to build a video
164
- project.
165
- """
166
-
167
- @property
168
- def parts(
169
- self
170
- ) -> list[_Part]:
171
- """
172
- The list of parts that build this track,
173
- but with the empty parts detected to
174
- be fulfilled with black frames and silent
175
- audios.
176
-
177
- A part can be a video or an empty space.
178
- """
179
- if (
180
- not hasattr(self, '_parts') or
181
- self._parts is None
182
- ):
183
- self._recalculate_parts()
184
-
185
- return self._parts
186
-
187
- @property
188
- def end(
189
- self
190
- ) -> Fraction:
191
- """
192
- The end of the last video of this track,
193
- which is also the end of the track. This
194
- is the last time moment that has to be
195
- rendered.
196
- """
197
- return Fraction(
198
- 0.0
199
- if len(self.videos) == 0 else
200
- max(
201
- video.end
202
- for video in self.videos
203
- )
204
- )
205
-
206
- @property
207
- def videos(
208
- self
209
- ) -> list[VideoOnTrack]:
210
- """
211
- The list of videos we have in the track
212
- but ordered using the 'start' attribute
213
- from first to last.
214
- """
215
- return sorted(self._videos, key = lambda video: video.start)
216
-
217
- @property
218
- def is_muted(
219
- self
220
- ) -> bool:
221
- """
222
- Flag to indicate if the track is muted or
223
- not. Being muted means that no audio frames
224
- will be retured from this track.
225
- """
226
- return self._is_muted
227
-
228
- def __init__(
229
- self,
230
- # TODO: I need the general settings of the
231
- # project to be able to make audio also, not
232
- # only the empty frames
233
- index: int,
234
- size: tuple[int, int],
235
- fps: float,
236
- audio_fps: float,
237
- # TODO: Where does it come from (?)
238
- audio_samples_per_frame: int,
239
- audio_layout: str = 'stereo',
240
- audio_format: str = 'fltp'
241
- ):
242
- self._videos: list[VideoOnTrack] = []
243
- """
244
- The list of 'VideoOnTrack' instances that
245
- must play on this track.
246
- """
247
- self._is_muted: bool = False
248
- """
249
- Internal flag to indicate if the track is
250
- muted or not.
251
- """
252
- self.size: tuple[int, int] = size
253
- """
254
- The size of the videos of this track.
255
- """
256
- self.index: int = index
257
- """
258
- The index of the track within the timeline.
259
- """
260
- self.fps: float = float(fps)
261
- """
262
- The fps of the track, needed to calculate
263
- the base t time moments to be precise and
264
- to obtain or generate the frames.
265
- """
266
- self.audio_fps: float = float(audio_fps)
267
- """
268
- The fps of the audio track, needed to
269
- generate silent audios for the empty parts.
270
- """
271
- self.audio_samples_per_frame: int = audio_samples_per_frame
272
- """
273
- The number of samples per audio frame.
274
- """
275
- self.audio_layout: str = audio_layout
276
- """
277
- The layout of the audio, that can be 'mono'
278
- or 'stereo'.
279
- """
280
- self.audio_format: str = audio_format
281
- """
282
- The format of the audio, that can be 's16',
283
- 'flt', 'fltp', etc.
284
- """
285
-
286
- def _is_free(
287
- self,
288
- start: Union[int, float, Fraction],
289
- end: Union[int, float, Fraction]
290
- ) -> bool:
291
- """
292
- Check if the time range in between the
293
- 'start' and 'end' time given is free or
294
- there is some video playing at any moment.
295
- """
296
- return not any(
297
- (
298
- video.start < end and
299
- video.end > start
300
- )
301
- for video in self.videos
302
- )
303
-
304
- def _get_part_at_t(
305
- self,
306
- t: Union[int, float, Fraction]
307
- ) -> _Part:
308
- """
309
- Get the part at the given 't' time
310
- moment, that will always exist because
311
- we have an special non ended last
312
- empty part that would be returned if
313
- accessing to an empty 't'.
314
- """
315
- for part in self.parts:
316
- if part.start <= t < part.end:
317
- return part
318
-
319
- # TODO: This will only happen if they are
320
- # asking for a value greater than the
321
- # NON_LIMITED_EMPTY_PART_END...
322
- raise Exception('NON_LIMITED_EMPTY_PART_END exceeded.')
323
- return None
324
-
325
- def mute(
326
- self
327
- ) -> 'Track':
328
- """
329
- Set the track as muted so no audio frame will
330
- be played from this track.
331
- """
332
- self._is_muted = True
333
-
334
- def unmute(
335
- self
336
- ) -> 'Track':
337
- """
338
- Set the track as unmuted so the audio frames
339
- will be played as normal.
340
- """
341
- self._is_muted = False
342
-
343
- def get_frame_at(
344
- self,
345
- t: Union[int, float, Fraction]
346
- ) -> 'VideoFrameWrapped':
347
- """
348
- Get the frame that must be displayed at
349
- the 't' time moment provided, which is
350
- a frame from the video audio that is
351
- being played at that time moment.
352
-
353
- Remember, this 't' time moment provided
354
- is about the track, and we make the
355
- conversion to the actual video 't' to
356
- get the frame.
357
- """
358
- # TODO: What if the frame, that comes from
359
- # a video, doesn't have the expected size (?)
360
- return self._get_part_at_t(t).get_frame_at(t)
361
-
362
- # TODO: This is not working well...
363
- def get_audio_frames_at(
364
- self,
365
- t: Union[int, float, Fraction]
366
- ):
367
- """
368
- Get the sequence of audio frames that
369
- must be displayed at the 't' time
370
- moment provided, which the collection
371
- of audio frames corresponding to the
372
- video frame that is being played at
373
- that time moment.
374
-
375
- Remember, this 't' time moment provided
376
- is about the track, and we make the
377
- conversion to the actual video 't' to
378
- get the frame.
379
-
380
- This is useful when we want to write a
381
- video frame with its audio, so we obtain
382
- all the audio frames associated to it
383
- (remember that a video frame is associated
384
- with more than 1 audio frame).
385
- """
386
- frames = (
387
- generate_silent_frames(
388
- fps = self.fps,
389
- audio_fps = self.audio_fps,
390
- audio_samples_per_frame = self.audio_samples_per_frame,
391
- layout = self.audio_layout,
392
- format = self.audio_format
393
- )
394
- if self.is_muted else
395
- self._get_part_at_t(t).get_audio_frames_at(t)
396
- )
397
-
398
- for frame in frames:
399
- yield frame
400
-
401
- def add_video(
402
- self,
403
- video: Video,
404
- t: Union[int, float, Fraction, None] = None
405
- ) -> 'Track':
406
- """
407
- Add the 'video' provided to the track. If
408
- a 't' time moment is provided, the video
409
- will be added to that time moment if
410
- possible. If there is no other video
411
- placed in the time gap between the given
412
- 't' and the provided 'video' duration, it
413
- will be added succesfully. In the other
414
- case, an exception will be raised.
415
-
416
- If 't' is None, the first available 't'
417
- time moment will be used, that will be 0.0
418
- if no video, or the end of the last video.
419
- """
420
- ParameterValidator.validate_mandatory_instance_of('video', video, Video)
421
- ParameterValidator.validate_positive_number('t', t, do_include_zero = True)
422
-
423
- if t is not None:
424
- # TODO: We can have many different strategies
425
- # that we could define in the '__init__' maybe
426
- t: T = T.from_fps(t, self.fps)
427
- #if not self._is_free(t.truncated, t.next(1).truncated):
428
- if not self._is_free(t.truncated, t.truncated + video.duration):
429
- raise Exception('The video cannot be added at the "t" time moment, something blocks it.')
430
- t = t.truncated
431
- else:
432
- t = self.end
433
-
434
- self._videos.append(VideoOnTrack(
435
- video,
436
- t
437
- ))
438
-
439
- self._recalculate_parts()
440
-
441
- # TODO: Maybe return the VideoOnTrack instead (?)
442
- return self
443
-
444
- def _recalculate_parts(
445
- self
446
- ) -> 'Track':
447
- """
448
- Check the track and get all the parts. A
449
- part can be empty (non video nor audio on
450
- that time period, which means black
451
- background and silence audio), or a video
452
- with (or without) audio.
453
- """
454
- parts = []
455
- cursor = 0.0
456
-
457
- for video in self.videos:
458
- # Empty space between cursor and start of
459
- # the next clip
460
- if video.start > cursor:
461
- parts.append(_Part(
462
- track = self,
463
- start = cursor,
464
- end = video.start,
465
- video = None
466
- ))
467
-
468
- # The video itself
469
- parts.append(_Part(
470
- track = self,
471
- start = video.start,
472
- end = video.end,
473
- video = video
474
- ))
475
-
476
- cursor = video.end
477
-
478
- # Add the non limited last empty part
479
- parts.append(_Part(
480
- track = self,
481
- start = cursor,
482
- end = NON_LIMITED_EMPTY_PART_END,
483
- video = None
484
- ))
485
-
486
- self._parts = parts
487
-
488
- return self
489
-
490
- # TODO: Is this method here ok (?)
491
- def generate_silent_frames(
492
- fps: int,
493
- audio_fps: int,
494
- audio_samples_per_frame: int,
495
- layout: str = 'stereo',
496
- format: str = 'fltp'
497
- ) -> list[AudioFrameWrapped]:
498
- """
499
- Get the audio silent frames we need for
500
- a video with the given 'fps', 'audio_fps'
501
- and 'audio_samples_per_frame', using the
502
- also provided 'layout' and 'format' for
503
- the audio frames.
504
-
505
- This method is used when we have empty
506
- parts on our tracks and we need to
507
- provide the frames, that are passed as
508
- AudioFrameWrapped instances and tagged as
509
- coming from empty parts.
510
- """
511
- audio_frame_generator: AudioFrameGenerator = AudioFrameGenerator()
512
-
513
- # Check how many full and partial silent
514
- # audio frames we need
515
- number_of_frames, number_of_remaining_samples = audio_frames_and_remainder_per_video_frame(
516
- video_fps = fps,
517
- sample_rate = audio_fps,
518
- number_of_samples_per_audio_frame = audio_samples_per_frame
519
- )
520
-
521
- # The complete silent frames we need
522
- silent_frame = audio_frame_generator.silent(
523
- sample_rate = audio_fps,
524
- layout = layout,
525
- number_of_samples = audio_samples_per_frame,
526
- format = format,
527
- pts = None,
528
- time_base = None
529
- )
530
-
531
- frames = (
532
- [
533
- AudioFrameWrapped(
534
- frame = silent_frame,
535
- is_from_empty_part = True
536
- )
537
- ] * number_of_frames
538
- if number_of_frames > 0 else
539
- []
540
- )
541
-
542
- # The remaining partial silent frames we need
543
- if number_of_remaining_samples > 0:
544
- silent_frame = audio_frame_generator.silent(
545
- sample_rate = audio_fps,
546
- # TODO: Check where do we get this value from
547
- layout = layout,
548
- number_of_samples = number_of_remaining_samples,
549
- # TODO: Check where do we get this value from
550
- format = format,
551
- pts = None,
552
- time_base = None
553
- )
554
-
555
- frames.append(
556
- AudioFrameWrapped(
557
- frame = silent_frame,
558
- is_from_empty_part = True
559
- )
560
- )
561
-
562
- return frames
@@ -1,27 +0,0 @@
1
- yta_video_opengl/__init__.py,sha256=ycAx_XYMVDfkuObSvtW6irQ0Wo-fgxEz3fjIRMe8PpY,205
2
- yta_video_opengl/classes.py,sha256=t5-Tfc7ecvHl8JlVBp_FVzZT6ole6Ly5-FeBBH7wcxo,37742
3
- yta_video_opengl/complete/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- yta_video_opengl/complete/frame_combinator.py,sha256=uYg7907knjBlmZUZCCzkxDcj0Nown0muvL5PNVS707A,9413
5
- yta_video_opengl/complete/frame_generator.py,sha256=VRcPgpqfxQWMeLzgEJObbM0xu7_85I1y_YyQVhcEswc,7853
6
- yta_video_opengl/complete/frame_wrapper.py,sha256=g0aTcUVmF5uQtxs95_XsxlwL0QUj-fNOSRHvK4ENqg4,3347
7
- yta_video_opengl/complete/timeline.py,sha256=d6_5Yd5n6TTjwo0ozKNDsd3GAfL12ATx0w-TVkIr0Eo,16767
8
- yta_video_opengl/complete/track.py,sha256=NCfNLOjg_7AWX5g5moYgAjmgAJ22Hp92TLa5pdoW6RM,17910
9
- yta_video_opengl/complete/video_on_track.py,sha256=laxDvMr1rrmnDfHpk825j42f4pj9I1X8vOFmzwteuM8,4824
10
- yta_video_opengl/nodes/__init__.py,sha256=TZ-ZO05PZ0_ABq675E22_PngLWOe-_w5s1cLlV3NbWM,3469
11
- yta_video_opengl/nodes/audio/__init__.py,sha256=4nKkC70k1UgLcCSPqFWm3cKdaJM0KUmQTwGWv1xFarQ,2926
12
- yta_video_opengl/nodes/video/__init__.py,sha256=gSoaoEmjdQmyRwH18mf5z3NAhap3S0RgbeBbfBXi4jc,132
13
- yta_video_opengl/nodes/video/opengl.py,sha256=K2pyCJEd9z4gnZqJetKyGPbtHuBzFsx74ZYyzhSqYPo,8510
14
- yta_video_opengl/reader/__init__.py,sha256=kKvOAEeDjIwAaWtpDEQHdAd_Gwk3Ssz2tv6gpNwVkQo,19644
15
- yta_video_opengl/reader/cache/__init__.py,sha256=PAfGM2J-8Vv6p6Cd9aAUvyBcw3rjx2gy_2pJO22VtDM,7020
16
- yta_video_opengl/reader/cache/audio.py,sha256=cm_1D5f5RnmJgaidA1pnEhTPF8DE0mU2MofmwjU_b5k,6781
17
- yta_video_opengl/reader/cache/utils.py,sha256=9aJ6qyUFRvoh2jRbIvtF_-1MOm_sgQtPiy0WXLCZYcA,1402
18
- yta_video_opengl/reader/cache/video.py,sha256=3sT9cE0sdTty5AE9yFAPJrJNxCX5vWVATK8OeJInr8I,3496
19
- yta_video_opengl/t.py,sha256=xOhT1xBEwChlXf-Tuy-WxA_08iRJWVlnL_Hyzr-9-sk,6633
20
- yta_video_opengl/tests.py,sha256=eyFnz7yBDJyIoti-cV7Dz1bMTeF61Z4_JrFkLXuZQl4,28019
21
- yta_video_opengl/utils.py,sha256=yUi17EjNR4SVpvdDUwUaKl4mBCb1uyFCSGoIX3Zr2F0,15586
22
- yta_video_opengl/video.py,sha256=Fgu_BzuDlMbfl1Hwjk8Yzo3ZxO73wPuyTUjTbf9OSLw,8951
23
- yta_video_opengl/writer.py,sha256=QwvjQcEkzn1WAVqVTFiI6tYIXJO67LKKUTJGO_eflFM,8893
24
- yta_video_opengl-0.0.19.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
25
- yta_video_opengl-0.0.19.dist-info/METADATA,sha256=wLhgCKzFD4EJiPuuCnNv40dimACB-Zkg8A7otla6ins,714
26
- yta_video_opengl-0.0.19.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
27
- yta_video_opengl-0.0.19.dist-info/RECORD,,