yta-video-frame-time 0.0.5__tar.gz → 0.0.6__tar.gz

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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: yta-video-frame-time
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Youtube Autonomous Video Frame Time Module
5
5
  Author: danialcala94
6
6
  Author-email: danielalcalavalera@gmail.com
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "yta-video-frame-time"
3
- version = "0.0.5"
3
+ version = "0.0.6"
4
4
  description = "Youtube Autonomous Video Frame Time Module"
5
5
  authors = [
6
6
  {name = "danialcala94",email = "danielalcalavalera@gmail.com"}
@@ -0,0 +1,725 @@
1
+ """
2
+ This is an example of what a video has:
3
+ - fps = 60
4
+ - time_base = 1 / 15360
5
+ - tick = fps * time_base = 256
6
+
7
+ So, the first pts is 0 and the second
8
+ one is 256. The frame 16 will be 3840,
9
+ that is 256 * 15 (because first index
10
+ is 0).
11
+ """
12
+ from yta_validation.parameter import ParameterValidator
13
+ from yta_validation.number import NumberValidator
14
+ from yta_validation import PythonValidator
15
+ from quicktions import Fraction
16
+ from typing import Union
17
+
18
+ import math
19
+
20
+
21
+ # TODO: This T class has to be removed
22
+ # and replaced by the THandler
23
+ class T:
24
+ """
25
+ Class to simplify the way we work with a
26
+ 't' time moment but using the fractions
27
+ library to be precise and avoid any issue
28
+ related with commas.
29
+
30
+ This class must be used when trying to
31
+ apply a specific 't' time moment for a
32
+ video or audio frame, using the fps or
33
+ sample rate as time_base to be precise.
34
+ """
35
+
36
+ @property
37
+ def truncated(
38
+ self
39
+ ) -> Fraction:
40
+ """
41
+ The 't' but as a Fraction that is multiple
42
+ of the given 'time_base' and truncated.
43
+ """
44
+ return round_t(self._t, self.time_base)
45
+
46
+ @property
47
+ def rounded(
48
+ self
49
+ ) -> Fraction:
50
+ """
51
+ The 't' but as a Fraction that is multiple
52
+ of the given 'time_base' and rounded (the
53
+ value could be the same as truncated if it
54
+ is closer to the previous value).
55
+ """
56
+ return round_t(self._t, self.time_base, do_truncate = False)
57
+
58
+ @property
59
+ def truncated_pts(
60
+ self
61
+ ) -> int:
62
+ """
63
+ The 'truncated' value but as a pts, which
64
+ is the int value to be set in audio and
65
+ video frames in the pyav library to be
66
+ displayed in that moment.
67
+ """
68
+ return int(self.truncated / self.time_base)
69
+
70
+ @property
71
+ def rounded_pts(
72
+ self
73
+ ) -> int:
74
+ """
75
+ The 'rounded' value but as a pts, which
76
+ is the int value to be set in audio and
77
+ video frames in the pyav library to be
78
+ displayed in that moment.
79
+ """
80
+ return int(self.rounded / self.time_base)
81
+
82
+ def __init__(
83
+ self,
84
+ t: Union[int, float, Fraction],
85
+ time_base: Fraction
86
+ ):
87
+ ParameterValidator.validate_mandatory_instance_of('t', t, [int, float, 'Fraction'])
88
+ ParameterValidator.validate_mandatory_instance_of('time_base', time_base, 'Fraction')
89
+
90
+ self._t: Union[int, float, Fraction] = t
91
+ """
92
+ The 't' time moment as it was passed as
93
+ parameter.
94
+ """
95
+ self.time_base: Fraction = time_base
96
+ """
97
+ The time_base that will used to round the
98
+ values to be multiples of it.
99
+ """
100
+
101
+ def next(
102
+ self,
103
+ n: int = 1
104
+ ) -> 'T':
105
+ """
106
+ Get the value that is 'n' times ahead of
107
+ the 'truncated' property of this instance.
108
+
109
+ Useful when you need the next value for a
110
+ range in an iteration or similar.
111
+ """
112
+ return T(self.truncated + n * self.time_base, self.time_base)
113
+
114
+ def previous(
115
+ self,
116
+ n: int = 1
117
+ ) -> 'T':
118
+ """
119
+ Get the value that is 'n' times before the
120
+ 'truncated' property of this instance.
121
+
122
+ Useful when you need the previous value to
123
+ check if the current is the next one or
124
+ similar.
125
+
126
+ Be careful, if the 'truncated' value is 0
127
+ this will give you an unexpected negative
128
+ value.
129
+ """
130
+ return T(self.truncated - n * self.time_base, self.time_base)
131
+
132
+ @staticmethod
133
+ def from_fps(
134
+ t: Union[int, float, Fraction],
135
+ fps: Union[int, float, Fraction]
136
+ ) -> 'T':
137
+ """
138
+ Get the instance but providing the 'fps'
139
+ (or sample rate) value directly, that will
140
+ be turned into a time base.
141
+ """
142
+ return T(t, fps_to_time_base(fps))
143
+
144
+ @staticmethod
145
+ def from_pts(
146
+ pts: int,
147
+ time_base: Fraction
148
+ ) -> 'T':
149
+ """
150
+ Get the instance but providing the 'pts'
151
+ and the 'time_base'.
152
+ """
153
+ return T(pts * time_base, time_base)
154
+
155
+
156
+ # TODO: This below is interesting, above
157
+ # is old...
158
+
159
+ def get_ts(
160
+ start: Union[int, float, Fraction],
161
+ end: Union[int, float, Fraction],
162
+ fps: Fraction
163
+ ) -> list[Fraction]:
164
+ """
165
+ Get all the 't' time moments between the given
166
+ 'start' and the given 'end', using the provided
167
+ 'time_base' for precision.
168
+
169
+ The 'end' is not included, we return a range
170
+ [start, end) because the last frame is the
171
+ start of another time range.
172
+ """
173
+ thandler = THandler(fps)
174
+
175
+ start = thandler.t.truncated(start)
176
+ end = thandler.t.truncated(end)
177
+
178
+ return [
179
+ start + i * thandler.time_base
180
+ for i in range((end - start) // thandler.time_base)
181
+ ]
182
+
183
+ def round_t(
184
+ t: Union[int, float, Fraction],
185
+ time_base = Fraction(1, 60),
186
+ do_truncate: bool = True
187
+ ) -> Fraction:
188
+ """
189
+ Round the given 't' time moment to the most
190
+ near multiple of the given 'time_base' (or
191
+ the previous one if 'do_truncate' is True)
192
+ using fractions module to be precise.
193
+
194
+ This method is very useful to truncate 't'
195
+ time moments in order to get the frames or
196
+ samples for the specific and exact time
197
+ moments according to their fps or sample
198
+ rate (that should be passed as the
199
+ 'time_base' parameter).
200
+
201
+ Examples below, with `time_base = 1/5`:
202
+ - `t = 0.25` => `0.2` (truncated or rounded)
203
+ - `t = 0.35` => `0.2` (truncated)
204
+ - `t = 0.45` => `0.4` (truncated or rounded)
205
+ - `t = 0.55` => `0.6` (rounded)
206
+ """
207
+ t = Fraction(t).limit_denominator()
208
+ steps = t / time_base
209
+
210
+ snapped_steps = (
211
+ steps.numerator // steps.denominator
212
+ if do_truncate else
213
+ round(steps) # round(float(steps))
214
+ )
215
+
216
+ return parse_fraction(snapped_steps * time_base)
217
+
218
+ def round_pts(
219
+ pts: int,
220
+ fps: Union[int, float, Fraction] = 60,
221
+ time_base = Fraction(1, 60),
222
+ do_truncate: bool = True
223
+ ) -> int:
224
+ """
225
+ Round the given 'pts' presentation
226
+ timestamp to the most near index
227
+ corresponding pts value (or the previous
228
+ one always if 'do_truncate' is True).
229
+
230
+ This method is very useful to truncate
231
+ 'pts' values in order to get the frames or
232
+ samples for the specific and exact time
233
+ moments according to their fps or sample
234
+ rate (that should be passed as the
235
+ 'time_base' parameter).
236
+
237
+ Pts value is calculated based on the 'fps'
238
+ and 'time_base', but here is an easier
239
+ example using the time moments.
240
+
241
+ Examples below, with `time_base = 1/5`:
242
+ - `t = 0.25` => `0.2` (truncated or rounded)
243
+ - `t = 0.35` => `0.2` (truncated)
244
+ - `t = 0.45` => `0.4` (truncated or rounded)
245
+ - `t = 0.55` => `0.6` (rounded)
246
+ """
247
+ ticks_per_frame = get_ticks_per_frame(fps, time_base)
248
+
249
+ frame_index = pts / ticks_per_frame
250
+
251
+ frame_index = (
252
+ math.floor(frame_index)
253
+ if do_truncate else
254
+ round(frame_index)
255
+ )
256
+
257
+ return int(frame_index * ticks_per_frame)
258
+
259
+ # TODO: Create a 'round_pts'
260
+
261
+ """
262
+ When we are working with the 't' time
263
+ moment we need to use the fps, and when
264
+ we are working with the 'pts' we need
265
+ to use the 'time_base'.
266
+ """
267
+
268
+ class _T:
269
+ """
270
+ Internal class to be used by the THandler
271
+ as a shortcut to the functionality
272
+ related with 't' values.
273
+ """
274
+
275
+ def __init__(
276
+ self,
277
+ t_handler: 'THandler'
278
+ ):
279
+ self._t_handler: THandler = t_handler
280
+ """
281
+ Instance of the parent THandler to
282
+ access to its properties.
283
+ """
284
+
285
+ def from_pts(
286
+ self,
287
+ pts: int,
288
+ do_truncate: Union[bool, None] = True
289
+ ) -> Fraction:
290
+ """
291
+ Get the 't' time moment for the frame
292
+ defined by the 'pts' presentation
293
+ timestamp.
294
+
295
+ If 't' is in a [start, end) range, we
296
+ will obtain the 'start' value if 't'
297
+ value is closer to it than to the 'end
298
+ value.
299
+
300
+ If 'do_truncate' is True, we will
301
+ always receive the 'start' value. If
302
+ None, we will not make any conversion
303
+ and the value received could be useless
304
+ because it is in the middle of a range.
305
+ """
306
+ pts = (
307
+ self._t_handler.pts.truncated(pts)
308
+ if do_truncate is True else
309
+ self._t_handler.pts.rounded(pts)
310
+ if do_truncate is False else
311
+ pts # if None
312
+ )
313
+
314
+ return Fraction(pts * self._t_handler.time_base)
315
+
316
+ def to_pts(
317
+ self,
318
+ t: Union[int, float, Fraction],
319
+ do_truncate: Union[bool, None] = True
320
+ ) -> int:
321
+ """
322
+ Transform the given 't' to a 'pts' value
323
+ truncating, rounding or applying no
324
+ variation.
325
+ """
326
+ return self._t_handler.pts.from_t(t, do_truncate)
327
+
328
+ def truncated(
329
+ self,
330
+ t: Union[int, float, Fraction]
331
+ ):
332
+ """
333
+ Get the 't' value provided but truncated.
334
+
335
+ This means that if 't' is in a
336
+ [start, end) range, we will obtain the
337
+ 'start' value always.
338
+ """
339
+ return round_t(t, Fraction(1, self._t_handler.fps), do_truncate = True)
340
+
341
+ def rounded(
342
+ self,
343
+ t: Union[int, float, Fraction]
344
+ ):
345
+ """
346
+ Get the 't' value provided but rounded.
347
+
348
+ This means that if 't' is in a
349
+ [start, end) range, we will obtain
350
+ the 'start' or the 'end' value according
351
+ to which one is closer to the that 't'
352
+ value provided.
353
+ """
354
+ return round_t(t, Fraction(1, self._t_handler.fps), do_truncate = False)
355
+
356
+ def next(
357
+ self,
358
+ t: Union[int, float, Fraction],
359
+ n: int = 1,
360
+ do_truncate: bool = True
361
+ ) -> Fraction:
362
+ """
363
+ Get the value that is 'n' times ahead of
364
+ the 't' property of this instance
365
+ (truncated or rounded according to the
366
+ 'do_truncate' parameter provided).
367
+
368
+ Useful when you need the next value for a
369
+ range in an iteration or similar.
370
+ """
371
+ t = (
372
+ self.truncated(t)
373
+ if do_truncate else
374
+ self.rounded(t)
375
+ )
376
+
377
+ return t + n * self._t_handler.time_base
378
+
379
+ def previous(
380
+ self,
381
+ t: Union[int, float, Fraction],
382
+ n: int = 1,
383
+ do_truncate: bool = True
384
+ ) -> Fraction:
385
+ """
386
+ Get the value that is 'n' times before
387
+ the 't' property of this instance
388
+ (truncated or rounded according to the
389
+ 'do_truncate' parameter provided).
390
+
391
+ Useful when you need the previous value to
392
+ check if the current is the next one or
393
+ similar.
394
+
395
+ Be careful, if the 'truncated' value is 0
396
+ this will give you an unexpected negative
397
+ value.
398
+ """
399
+ t = (
400
+ self.truncated(t)
401
+ if do_truncate else
402
+ self.rounded(t)
403
+ )
404
+
405
+ return t - n * self._t_handler.time_base
406
+
407
+ class _Pts:
408
+ """
409
+ Internal class to be used by the THandler
410
+ as a shortcut to the functionality
411
+ related with 'pts' values.
412
+ """
413
+
414
+ def __init__(
415
+ self,
416
+ t_handler: 'THandler'
417
+ ):
418
+ self._t_handler: THandler = t_handler
419
+ """
420
+ Instance of the parent THandler to
421
+ access to its properties.
422
+ """
423
+
424
+ def from_t(
425
+ self,
426
+ t: Union[int, float, Fraction],
427
+ do_truncate: Union[bool, None] = True
428
+ ) -> int:
429
+ """
430
+ Get the pts (the amount of accumulated
431
+ ticks, also called presentation timestamp),
432
+ for the frame defined by the 't' time
433
+ moment provided.
434
+
435
+ If 't' is in a [start, end) range, we
436
+ will obtain the 'start' value if 't'
437
+ value is closer to it than to the 'end
438
+ value.
439
+
440
+ If 'do_truncate' is True, we will
441
+ always receive the 'start' value. If
442
+ None, we will not make any conversion
443
+ and the value received could be useless
444
+ because it is in the middle of a range.
445
+ """
446
+ t = (
447
+ self._t_handler.t.truncated(t)
448
+ if do_truncate is True else
449
+ self._t_handler.t.rounded(t)
450
+ if do_truncate is False else
451
+ t # if None
452
+ )
453
+
454
+ return int(t / self._t_handler.time_base)
455
+
456
+ def to_t(
457
+ self,
458
+ pts: int,
459
+ do_truncate: Union[bool, None] = True
460
+ ) -> Fraction:
461
+ """
462
+ Transform the given 'pts' to a 't' value
463
+ truncating, rounding or applying no
464
+ variation.
465
+ """
466
+ return self._t_handler.t.from_pts(pts, do_truncate)
467
+
468
+ def truncated(
469
+ self,
470
+ pts: int
471
+ ):
472
+ """
473
+ Get the 'pts' value provided but truncated.
474
+
475
+ This means that if 't' is in a
476
+ [start, end) range, we will obtain the
477
+ 'start' value always.
478
+ """
479
+ return round_pts(
480
+ pts = pts,
481
+ fps = self._t_handler.fps,
482
+ time_base = self._t_handler.time_base,
483
+ do_truncate = True
484
+ )
485
+
486
+ def rounded(
487
+ self,
488
+ pts: int
489
+ ) -> int:
490
+ """
491
+ Get the 'pts' value provided but rounded.
492
+
493
+ This means that if 't' is in a
494
+ [start, end) range, we will obtain
495
+ the 'start' or the 'end' value according
496
+ to which one is closer to the that 't'
497
+ value provided.
498
+ """
499
+ return round_pts(
500
+ pts = pts,
501
+ fps = self._t_handler.fps,
502
+ time_base = self._t_handler.time_base,
503
+ do_truncate = False
504
+ )
505
+
506
+ def next(
507
+ self,
508
+ pts: int,
509
+ n: int = 1,
510
+ do_truncate: bool = True
511
+ ) -> int:
512
+ """
513
+ Get the value that is 'n' times ahead of
514
+ the 'pts' value provided (truncated or
515
+ rounded according to the 'do_truncate'
516
+ parameter provided).
517
+
518
+ Useful when you need the next value for a
519
+ range in an iteration or similar.
520
+ """
521
+ pts = (
522
+ self.truncated(pts)
523
+ if do_truncate else
524
+ self.rounded(pts)
525
+ )
526
+
527
+ return pts + n * get_ticks_per_frame(self._t_handler.fps, self._t_handler.time_base)
528
+
529
+ def previous(
530
+ self,
531
+ pts: int,
532
+ n: int = 1,
533
+ do_truncate: bool = True
534
+ ) -> int:
535
+ """
536
+ Get the value that is 'n' times before
537
+ the 't' property of this instance
538
+ (truncated or rounded according to the
539
+ 'do_truncate' parameter provided).
540
+
541
+ Useful when you need the previous value to
542
+ check if the current is the next one or
543
+ similar.
544
+
545
+ Be careful, if the 'truncated' value is 0
546
+ this will give you an unexpected negative
547
+ value.
548
+ """
549
+ pts = (
550
+ self.truncated(pts)
551
+ if do_truncate else
552
+ self.rounded(pts)
553
+ )
554
+
555
+ return pts - n * get_ticks_per_frame(self._t_handler.fps, self._t_handler.time_base)
556
+
557
+ class THandler:
558
+ """
559
+ Class to simplify the way we work with
560
+ pyav frames time moments, indexes and
561
+ pts values.
562
+
563
+ This is an example of what a video has:
564
+ - `fps = 60`
565
+ - `time_base = 1 / 15360`
566
+ - `tick = fps * time_base = 256`
567
+
568
+ So, considering this above:
569
+ - Frame #1: `pts[0] = 256 * 0 = 0`
570
+ - Frame #2: `pts[1] = 256 * 1 = 256`
571
+ - Frame #16: `pts[15] = 256 * 15 = 3840`
572
+ """
573
+
574
+ def __init__(
575
+ self,
576
+ fps: Union[int, float, Fraction],
577
+ time_base: Union[Fraction, None] = None
578
+ ):
579
+ """
580
+ If the 'time_base' provided is None it will
581
+ be automatically `1/fps`.
582
+ """
583
+ ParameterValidator.validate_mandatory_positive_number('fps', fps, do_include_zero = False)
584
+ ParameterValidator.validate_instance_of('time_base', time_base, 'Fraction')
585
+
586
+ self.fps: Fraction = parse_fraction(fps)
587
+ """
588
+ The frames per second.
589
+ """
590
+ self.time_base: Fraction = (
591
+ time_base
592
+ if time_base is not None else
593
+ fps_to_time_base(self.fps)
594
+ )
595
+ """
596
+ The time base.
597
+ """
598
+ self.t: _T = _T(self)
599
+ """
600
+ Shortcut to the instance that handles
601
+ the 't' related functionality.
602
+ """
603
+ self.pts: _Pts = _Pts(self)
604
+ """
605
+ Shortcut to the instance that handles
606
+ the 'pts' related functionality.
607
+ """
608
+
609
+ # TODO: I think I should create a THandler
610
+ # that receives 'fps' and 'time_base' and
611
+ # then, by passing a 't' value, we can
612
+ # calculate everything we need, so we
613
+ # simplify all these processes
614
+ def frame_t_to_index(
615
+ t: Union[float, int, Fraction],
616
+ fps: Union[float, int, Fraction]
617
+ ) -> int:
618
+ """
619
+ Get the index of the frame with the
620
+ given 't' time moment, based on the
621
+ also provided 'fps'.
622
+
623
+ The formula:
624
+ - `int(t * fps)`
625
+ """
626
+ return int(parse_fraction(t) * fps)
627
+
628
+ def frame_index_to_t(
629
+ index: int,
630
+ fps: Union[float, int, Fraction]
631
+ ):
632
+ """
633
+ Get the 't' time moment for the frame
634
+ with the given 'index', based on the
635
+ also provided 'fps'.
636
+
637
+ The formula:
638
+ - `frame_index * (1 / fps)`
639
+ """
640
+ return index * parse_fraction(1, parse_fraction(fps))
641
+
642
+ def frame_t_to_pts(
643
+ t: Union[float, int, Fraction],
644
+ fps: Union[float, int, Fraction],
645
+ time_base: Fraction
646
+ ):
647
+ """
648
+ Get the pts (the amount of accumulated
649
+ ticks, also called presentation timestamp),
650
+ for the frame defined by the 't' time
651
+ moment provided, based on the also provided
652
+ 'fps' and 'time_base'.
653
+
654
+ The formula:
655
+ - `frame_index * ticks_per_frame`
656
+ """
657
+ return frame_t_to_index(t, fps) * get_ticks_per_frame(fps, time_base)
658
+
659
+ def frame_pts_to_t(
660
+ pts: int,
661
+ time_base: Fraction
662
+ ) -> Fraction:
663
+ """
664
+ Get the 't' time moment of the frame with
665
+ the given 'pts' (the amount of accumulated
666
+ ticks, also called presentation timestamp),
667
+ based on the also provided 'time_base'.
668
+
669
+ The formula:
670
+ - `pts * time_base`
671
+ """
672
+ return parse_fraction(pts * time_base)
673
+
674
+ def get_ticks_per_frame(
675
+ fps: Union[float, int, Fraction],
676
+ time_base: Fraction
677
+ ) -> int:
678
+ """
679
+ Get the amount of ticks per frame. A
680
+ tick is the minimum amount of time we
681
+ spend from one frame to the next.
682
+
683
+ The formula:
684
+ - `1 / (fps * time_base)`
685
+ """
686
+ return int(Fraction(1, 1) / (fps * time_base))
687
+
688
+ def fps_to_frame_duration(
689
+ fps: Union[float, int, Fraction]
690
+ ) -> Fraction:
691
+ """
692
+ Get the frame duration based on the 'fps'
693
+ provided.
694
+
695
+ The formula:
696
+ - `1 / fps`
697
+ """
698
+ return Fraction(1, parse_fraction(fps))
699
+
700
+ def fps_to_time_base(
701
+ fps: Union[float, int, Fraction]
702
+ ) -> Fraction:
703
+ """
704
+ Get the time base based on the given 'fps',
705
+ that will be basically `1/fps`. This is a
706
+ bit useless, just when we don't want to
707
+ think too much to use a time base and we
708
+ want to use the fps.
709
+
710
+ The formula:
711
+ - `1 / fps`
712
+ """
713
+ return Fraction(1, parse_fraction(fps))
714
+
715
+ def parse_fraction(
716
+ value: Union[float, int, Fraction]
717
+ ) -> Fraction:
718
+ """
719
+ Parse the provided 'value' as a Fraction
720
+ and limits its denominator.
721
+ """
722
+ fraction = Fraction(value)#.limit_denominator(100_000)
723
+
724
+ return fraction
725
+
@@ -1,234 +0,0 @@
1
- """
2
- This is an example of what a video has:
3
- - fps = 60
4
- - time_base = 1 / 15360
5
- - tick = fps * time_base = 256
6
-
7
- So, the first pts is 0 and the second
8
- one is 256. The frame 16 will be 3840,
9
- that is 256 * 15 (because first index
10
- is 0).
11
- """
12
- from yta_validation.parameter import ParameterValidator
13
- from yta_validation.number import NumberValidator
14
- from yta_validation import PythonValidator
15
- from quicktions import Fraction
16
- from typing import Union
17
-
18
-
19
- class T:
20
- """
21
- Class to simplify the way we work with a
22
- 't' time moment but using the fractions
23
- library to be precise and avoid any issue
24
- related with commas.
25
-
26
- This class must be used when trying to
27
- apply a specific 't' time moment for a
28
- video or audio frame, using the fps or
29
- sample rate as time_base to be precise.
30
- """
31
-
32
- @property
33
- def truncated(
34
- self
35
- ) -> Fraction:
36
- """
37
- The 't' but as a Fraction that is multiple
38
- of the given 'time_base' and truncated.
39
- """
40
- return round_t(self._t, self.time_base)
41
-
42
- @property
43
- def rounded(
44
- self
45
- ) -> Fraction:
46
- """
47
- The 't' but as a Fraction that is multiple
48
- of the given 'time_base' and rounded (the
49
- value could be the same as truncated if it
50
- is closer to the previous value).
51
- """
52
- return round_t(self._t, self.time_base, do_truncate = False)
53
-
54
- @property
55
- def truncated_pts(
56
- self
57
- ) -> int:
58
- """
59
- The 'truncated' value but as a pts, which
60
- is the int value to be set in audio and
61
- video frames in the pyav library to be
62
- displayed in that moment.
63
- """
64
- return int(self.truncated / self.time_base)
65
-
66
- @property
67
- def rounded_pts(
68
- self
69
- ) -> int:
70
- """
71
- The 'rounded' value but as a pts, which
72
- is the int value to be set in audio and
73
- video frames in the pyav library to be
74
- displayed in that moment.
75
- """
76
- return int(self.rounded / self.time_base)
77
-
78
- def __init__(
79
- self,
80
- t: Union[int, float, Fraction],
81
- time_base: Fraction
82
- ):
83
- ParameterValidator.validate_mandatory_instance_of('t', t, [int, float, 'Fraction'])
84
- ParameterValidator.validate_mandatory_instance_of('time_base', time_base, 'Fraction')
85
-
86
- self._t: Union[int, float, Fraction] = t
87
- """
88
- The 't' time moment as it was passed as
89
- parameter.
90
- """
91
- self.time_base: Fraction = time_base
92
- """
93
- The time_base that will used to round the
94
- values to be multiples of it.
95
- """
96
-
97
- def next(
98
- self,
99
- n: int = 1
100
- ) -> 'T':
101
- """
102
- Get the value that is 'n' times ahead of
103
- the 'truncated' property of this instance.
104
-
105
- Useful when you need the next value for a
106
- range in an iteration or similar.
107
- """
108
- return T(self.truncated + n * self.time_base, self.time_base)
109
-
110
- def previous(
111
- self,
112
- n: int = 1
113
- ) -> 'T':
114
- """
115
- Get the value that is 'n' times before the
116
- 'truncated' property of this instance.
117
-
118
- Useful when you need the previous value to
119
- check if the current is the next one or
120
- similar.
121
-
122
- Be careful, if the 'truncated' value is 0
123
- this will give you an unexpected negative
124
- value.
125
- """
126
- return T(self.truncated - n * self.time_base, self.time_base)
127
-
128
- @staticmethod
129
- def from_fps(
130
- t: Union[int, float, Fraction],
131
- fps: Union[int, float, Fraction]
132
- ) -> 'T':
133
- """
134
- Get the instance but providing the 'fps'
135
- (or sample rate) value directly, that will
136
- be turned into a time base.
137
- """
138
- return T(t, fps_to_time_base(fps))
139
-
140
- @staticmethod
141
- def from_pts(
142
- pts: int,
143
- time_base: Fraction
144
- ) -> 'T':
145
- """
146
- Get the instance but providing the 'pts'
147
- and the 'time_base'.
148
- """
149
- return T(pts * time_base, time_base)
150
-
151
-
152
- # TODO: Careful with this below
153
- """
154
- To obtain the pts step, or frame duration in
155
- ticks, you need to apply 2 formulas that are
156
- different according to if the frame is video
157
- or audio:
158
- - Audio: .samples
159
- - Video: int(round((1 / .fps) / .time_base))
160
- """
161
-
162
- def get_ts(
163
- start: Union[int, float, Fraction],
164
- end: Union[int, float, Fraction],
165
- fps: Fraction
166
- ) -> list[Fraction]:
167
- """
168
- Get all the 't' time moments between the given
169
- 'start' and the given 'end', using the provided
170
- 'time_base' for precision.
171
-
172
- The 'end' is not included, we return a range
173
- [start, end) because the last frame is the
174
- start of another time range.
175
- """
176
- start = T.from_fps(start, fps).truncated
177
- end = T.from_fps(end, fps).truncated
178
-
179
- time_base = fps_to_time_base(fps)
180
-
181
- return [
182
- start + i * time_base
183
- for i in range((end - start) // time_base)
184
- ]
185
-
186
- def round_t(
187
- t: Union[int, float, Fraction],
188
- time_base = Fraction(1, 60),
189
- do_truncate: bool = True
190
- ):
191
- """
192
- Round the given 't' time moment to the most
193
- near multiple of the given 'time_base' (or
194
- the previous one if 'do_truncate' is True)
195
- using fractions module to be precise.
196
-
197
- This method is very useful to truncate 't'
198
- time moments in order to get the frames or
199
- samples for the specific and exact time
200
- moments according to their fps or sample
201
- rate (that should be passed as the
202
- 'time_base' parameter).
203
-
204
- Examples below, with `time_base = 1/5`:
205
- - `t = 0.25` => `0.2` (truncated or rounded)
206
- - `t = 0.35` => `0.2` (truncated)
207
- - `t = 0.45` => `0.4` (truncated or rounded)
208
- - `t = 0.55` => `0.6` (rounded)
209
- """
210
- t = Fraction(t).limit_denominator()
211
- steps = t / time_base
212
-
213
- snapped_steps = (
214
- steps.numerator // steps.denominator
215
- if do_truncate else
216
- round(steps) # round(float(steps))
217
- )
218
-
219
- return snapped_steps * time_base
220
-
221
- def fps_to_time_base(
222
- fps: Union[int, float, Fraction]
223
- ) -> Fraction:
224
- """
225
- Get the pyav time base from the given
226
- 'fps'.
227
- """
228
- return (
229
- Fraction(1, fps)
230
- if NumberValidator.is_int(fps) else
231
- Fraction(1, 1) / fps
232
- if PythonValidator.is_instance_of(fps, 'Fraction') else
233
- Fraction(1, 1) / Fraction.from_float(fps).limit_denominator(1000000) # if float
234
- )