yta-video-frame-time 0.0.4__py3-none-any.whl → 0.0.6__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.
Potentially problematic release.
This version of yta-video-frame-time might be problematic. Click here for more details.
- yta_video_frame_time/__init__.py +0 -1
- yta_video_frame_time/t_fraction.py +725 -0
- {yta_video_frame_time-0.0.4.dist-info → yta_video_frame_time-0.0.6.dist-info}/METADATA +2 -1
- yta_video_frame_time-0.0.6.dist-info/RECORD +6 -0
- yta_video_frame_time-0.0.4.dist-info/RECORD +0 -5
- {yta_video_frame_time-0.0.4.dist-info → yta_video_frame_time-0.0.6.dist-info}/LICENSE +0 -0
- {yta_video_frame_time-0.0.4.dist-info → yta_video_frame_time-0.0.6.dist-info}/WHEEL +0 -0
yta_video_frame_time/__init__.py
CHANGED
|
@@ -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,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: yta-video-frame-time
|
|
3
|
-
Version: 0.0.
|
|
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
|
|
7
7
|
Requires-Python: ==3.9
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Requires-Dist: quicktions (>=0.0.1,<9.0.0)
|
|
10
11
|
Requires-Dist: yta_validation (>=0.0.1,<1.0.0)
|
|
11
12
|
Description-Content-Type: text/markdown
|
|
12
13
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
yta_video_frame_time/__init__.py,sha256=-YOa7lOKdiA3FwDEHHU1tHobnmhjFpTaVLfJQLZqoMI,22252
|
|
2
|
+
yta_video_frame_time/t_fraction.py,sha256=c0sz-ncVV_Qt--rM2k1xKyjZpPF_pH5bFKPFx6WmxQc,20027
|
|
3
|
+
yta_video_frame_time-0.0.6.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
|
4
|
+
yta_video_frame_time-0.0.6.dist-info/METADATA,sha256=is6UfhRMg48ToFwXBgwHOuPT2K6aqGTSt05KedCnAiw,515
|
|
5
|
+
yta_video_frame_time-0.0.6.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
6
|
+
yta_video_frame_time-0.0.6.dist-info/RECORD,,
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
yta_video_frame_time/__init__.py,sha256=xgdMlHkHFw__0q9FdFFcjcFQIIeIw_xTzw6YS-9SyI4,22254
|
|
2
|
-
yta_video_frame_time-0.0.4.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
|
3
|
-
yta_video_frame_time-0.0.4.dist-info/METADATA,sha256=C9Qqb5ai6V4mvvIUNwknag5c0quMy8Tmf4u-rH-vBWE,472
|
|
4
|
-
yta_video_frame_time-0.0.4.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
5
|
-
yta_video_frame_time-0.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|