yta-video-opengl 0.0.13__py3-none-any.whl → 0.0.15__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.
- yta_video_opengl/complete/timeline.py +3 -4
- yta_video_opengl/complete/track.py +3 -5
- yta_video_opengl/complete/video_on_track.py +2 -8
- yta_video_opengl/reader/__init__.py +22 -39
- yta_video_opengl/reader/cache/__init__.py +249 -0
- yta_video_opengl/reader/cache/audio.py +195 -0
- yta_video_opengl/reader/cache/utils.py +48 -0
- yta_video_opengl/reader/cache/video.py +110 -0
- yta_video_opengl/t.py +55 -7
- yta_video_opengl/video.py +9 -4
- {yta_video_opengl-0.0.13.dist-info → yta_video_opengl-0.0.15.dist-info}/METADATA +1 -1
- yta_video_opengl-0.0.15.dist-info/RECORD +24 -0
- yta_video_opengl/reader/cache.py +0 -529
- yta_video_opengl-0.0.13.dist-info/RECORD +0 -21
- {yta_video_opengl-0.0.13.dist-info → yta_video_opengl-0.0.15.dist-info}/LICENSE +0 -0
- {yta_video_opengl-0.0.13.dist-info → yta_video_opengl-0.0.15.dist-info}/WHEEL +0 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
|
2
|
+
from yta_video_opengl.reader.cache import FrameCache
|
3
|
+
from yta_video_opengl.t import T
|
4
|
+
from yta_validation.parameter import ParameterValidator
|
5
|
+
from av.container import InputContainer
|
6
|
+
from av.video.stream import VideoStream
|
7
|
+
from av.video.frame import VideoFrame
|
8
|
+
from quicktions import Fraction
|
9
|
+
from typing import Union
|
10
|
+
|
11
|
+
|
12
|
+
class VideoFrameCache(FrameCache):
|
13
|
+
"""
|
14
|
+
Cache for the video frames.
|
15
|
+
"""
|
16
|
+
|
17
|
+
@property
|
18
|
+
def fps(
|
19
|
+
self
|
20
|
+
) -> Union[Fraction, None]:
|
21
|
+
"""
|
22
|
+
The frames per second.
|
23
|
+
"""
|
24
|
+
return self.stream.average_rate
|
25
|
+
|
26
|
+
@property
|
27
|
+
def frame_duration(
|
28
|
+
self
|
29
|
+
) -> int:
|
30
|
+
"""
|
31
|
+
The frame duration in ticks, which is the
|
32
|
+
minimum amount of time, 1 / time_base.
|
33
|
+
"""
|
34
|
+
return self.stream.duration / self.stream.frames
|
35
|
+
|
36
|
+
def __init__(
|
37
|
+
self,
|
38
|
+
container: InputContainer,
|
39
|
+
stream: VideoStream,
|
40
|
+
size: Union[int, None] = None
|
41
|
+
):
|
42
|
+
ParameterValidator.validate_mandatory_instance_of('stream', stream, VideoStream)
|
43
|
+
|
44
|
+
super().__init__(container, stream, size)
|
45
|
+
|
46
|
+
def get_frame(
|
47
|
+
self,
|
48
|
+
t: Union[int, float, Fraction]
|
49
|
+
) -> VideoFrame:
|
50
|
+
"""
|
51
|
+
Get the video frame that is in the 't'
|
52
|
+
time moment provided.
|
53
|
+
"""
|
54
|
+
t: T = T.from_fps(t, self.fps)
|
55
|
+
for frame in self.get_frames(t.truncated, t.next(1).truncated):
|
56
|
+
return frame
|
57
|
+
|
58
|
+
def get_frames(
|
59
|
+
self,
|
60
|
+
start: Union[int, float, Fraction],
|
61
|
+
end: Union[int, float, Fraction]
|
62
|
+
):
|
63
|
+
"""
|
64
|
+
Get all the frames in the range between
|
65
|
+
the provided 'start' and 'end' time in
|
66
|
+
seconds.
|
67
|
+
|
68
|
+
This method is an iterator that yields
|
69
|
+
the frame, its t and its index.
|
70
|
+
"""
|
71
|
+
# TODO: Validate 'start' and 'end' are mandatory
|
72
|
+
# positive numbers
|
73
|
+
# Make sure the 'start' and 'end' time moments
|
74
|
+
# provided are truncated values based on the
|
75
|
+
# stream time base
|
76
|
+
start = T(start, self.time_base).truncated
|
77
|
+
end = T(end, self.time_base).truncated
|
78
|
+
|
79
|
+
if end <= start:
|
80
|
+
raise Exception(f'The time range start:{str(float(start))} - end:{str(float(end))}) is not valid.')
|
81
|
+
|
82
|
+
key_frame_pts = self._get_nearest_keyframe_pts(start / self.time_base)
|
83
|
+
|
84
|
+
if (
|
85
|
+
self._last_packet_accessed is None or
|
86
|
+
self._last_packet_accessed.pts != key_frame_pts
|
87
|
+
):
|
88
|
+
self._seek(key_frame_pts)
|
89
|
+
|
90
|
+
for packet in self.container.demux(self.stream):
|
91
|
+
if packet.pts is None:
|
92
|
+
continue
|
93
|
+
|
94
|
+
self._last_packet_accessed = packet
|
95
|
+
|
96
|
+
for frame in packet.decode():
|
97
|
+
if frame.pts is None:
|
98
|
+
continue
|
99
|
+
|
100
|
+
# We store all the frames in cache
|
101
|
+
self._store_frame_in_cache(frame)
|
102
|
+
|
103
|
+
current_frame_time = frame.pts * self.time_base
|
104
|
+
|
105
|
+
# We want the range [start, end)
|
106
|
+
if start <= current_frame_time < end:
|
107
|
+
yield frame
|
108
|
+
|
109
|
+
if current_frame_time >= end:
|
110
|
+
break
|
yta_video_opengl/t.py
CHANGED
@@ -1,3 +1,14 @@
|
|
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
|
+
"""
|
1
12
|
from yta_validation.parameter import ParameterValidator
|
2
13
|
from yta_validation import PythonValidator
|
3
14
|
from yta_validation.number import NumberValidator
|
@@ -36,7 +47,7 @@ class T:
|
|
36
47
|
The 't' but as a Fraction that is multiple
|
37
48
|
of the given 'time_base' and rounded (the
|
38
49
|
value could be the same as truncated if it
|
39
|
-
is closer to the
|
50
|
+
is closer to the previous value).
|
40
51
|
"""
|
41
52
|
return round_t(self._t, self.time_base, do_truncate = False)
|
42
53
|
|
@@ -96,20 +107,57 @@ class T:
|
|
96
107
|
"""
|
97
108
|
return T(self.truncated + n * self.time_base, self.time_base)
|
98
109
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
+
|
103
128
|
@staticmethod
|
104
129
|
def from_fps(
|
105
130
|
t: Union[int, float, Fraction],
|
106
131
|
fps: Union[int, float, Fraction]
|
107
|
-
):
|
132
|
+
) -> 'T':
|
108
133
|
"""
|
109
134
|
Get the instance but providing the 'fps'
|
110
|
-
(or sample rate) value directly
|
135
|
+
(or sample rate) value directly, that will
|
136
|
+
be turned into a time base.
|
111
137
|
"""
|
112
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
|
+
"""
|
113
161
|
|
114
162
|
def get_ts(
|
115
163
|
start: Union[int, float, Fraction],
|
yta_video_opengl/video.py
CHANGED
@@ -182,17 +182,22 @@ class Video:
|
|
182
182
|
Get the video frame with the given 't' time
|
183
183
|
moment, using the video cache system.
|
184
184
|
"""
|
185
|
-
return self.reader.video_cache.
|
186
|
-
|
185
|
+
return self.reader.video_cache.get_frame(self._get_real_t(t))
|
186
|
+
|
187
187
|
def get_audio_frame_from_t(
|
188
188
|
self,
|
189
189
|
t: Union[int, float, Fraction]
|
190
190
|
) -> 'AudioFrame':
|
191
191
|
"""
|
192
192
|
Get the audio frame with the given 't' time
|
193
|
-
moment, using the audio cache system.
|
193
|
+
moment, using the audio cache system. This
|
194
|
+
method is useful when we need to combine
|
195
|
+
many different frames so we can obtain them
|
196
|
+
one by one.
|
197
|
+
|
198
|
+
TODO: Is this actually necessary (?)
|
194
199
|
"""
|
195
|
-
return self.reader.
|
200
|
+
return self.reader.get_audio_frame_from_t(self._get_real_t(t))
|
196
201
|
|
197
202
|
def get_audio_frames_from_t(
|
198
203
|
self,
|
@@ -0,0 +1,24 @@
|
|
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/timeline.py,sha256=y2IPLRlk3ix14zflCsLKWajbqtf6Y8ceStpLs69OqHY,9323
|
5
|
+
yta_video_opengl/complete/track.py,sha256=hI1b4EmbXMPB_COxNUs1VlHVPtbiaIgvt37RY-YUz7g,13623
|
6
|
+
yta_video_opengl/complete/video_on_track.py,sha256=KROAI0bndnfcvKlHGsSEyWg9o1xozW0PI_Rhqp0r9kw,4844
|
7
|
+
yta_video_opengl/nodes/__init__.py,sha256=TZ-ZO05PZ0_ABq675E22_PngLWOe-_w5s1cLlV3NbWM,3469
|
8
|
+
yta_video_opengl/nodes/audio/__init__.py,sha256=4nKkC70k1UgLcCSPqFWm3cKdaJM0KUmQTwGWv1xFarQ,2926
|
9
|
+
yta_video_opengl/nodes/video/__init__.py,sha256=gSoaoEmjdQmyRwH18mf5z3NAhap3S0RgbeBbfBXi4jc,132
|
10
|
+
yta_video_opengl/nodes/video/opengl.py,sha256=K2pyCJEd9z4gnZqJetKyGPbtHuBzFsx74ZYyzhSqYPo,8510
|
11
|
+
yta_video_opengl/reader/__init__.py,sha256=kKvOAEeDjIwAaWtpDEQHdAd_Gwk3Ssz2tv6gpNwVkQo,19644
|
12
|
+
yta_video_opengl/reader/cache/__init__.py,sha256=PAfGM2J-8Vv6p6Cd9aAUvyBcw3rjx2gy_2pJO22VtDM,7020
|
13
|
+
yta_video_opengl/reader/cache/audio.py,sha256=cm_1D5f5RnmJgaidA1pnEhTPF8DE0mU2MofmwjU_b5k,6781
|
14
|
+
yta_video_opengl/reader/cache/utils.py,sha256=9aJ6qyUFRvoh2jRbIvtF_-1MOm_sgQtPiy0WXLCZYcA,1402
|
15
|
+
yta_video_opengl/reader/cache/video.py,sha256=CSVgb3Sjqzk22sQkukoakVzms-wwZpXOT61Y6tirhjg,3292
|
16
|
+
yta_video_opengl/t.py,sha256=xOhT1xBEwChlXf-Tuy-WxA_08iRJWVlnL_Hyzr-9-sk,6633
|
17
|
+
yta_video_opengl/tests.py,sha256=EdTyYtTUd_mj6geWnrvnF-wZSHCKKvhYgiLclkV73O0,26576
|
18
|
+
yta_video_opengl/utils.py,sha256=yUi17EjNR4SVpvdDUwUaKl4mBCb1uyFCSGoIX3Zr2F0,15586
|
19
|
+
yta_video_opengl/video.py,sha256=JPIWDQcYlLi8eT2LOFQtS1jVu5xVmW4bz1VMtP0gMeA,8626
|
20
|
+
yta_video_opengl/writer.py,sha256=QwvjQcEkzn1WAVqVTFiI6tYIXJO67LKKUTJGO_eflFM,8893
|
21
|
+
yta_video_opengl-0.0.15.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
22
|
+
yta_video_opengl-0.0.15.dist-info/METADATA,sha256=nDTKhQqRMXHzjg8kQWvRWKfvDsP7rvsAhpcl8qayl-8,714
|
23
|
+
yta_video_opengl-0.0.15.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
24
|
+
yta_video_opengl-0.0.15.dist-info/RECORD,,
|