23
23
class VideoStreamMetadata :
24
24
"""Metadata of a single video stream."""
25
25
26
- duration_seconds : Optional [float ]
27
- """Duration of the stream, in seconds (float or None)."""
26
+ duration_seconds_from_header : Optional [float ]
27
+ """Duration of the stream, in seconds obtained from the header (float or
28
+ None). This could be inaccurate."""
28
29
bit_rate : Optional [float ]
29
30
"""Bit rate of the stream, in seconds (float or None)."""
30
31
num_frames_from_header : Optional [int ]
@@ -36,17 +37,27 @@ class VideoStreamMetadata:
36
37
content (the scan doesn't involve decoding). This is more accurate
37
38
than ``num_frames_from_header``. We recommend using the
38
39
``num_frames`` attribute instead. (int or None)."""
39
- min_pts_seconds : Optional [float ]
40
- """Minimum :term:`pts` of any frame in the stream (float or None)."""
41
- max_pts_seconds : Optional [float ]
42
- """Maximum :term:`pts` of any frame in the stream (float or None)."""
40
+ begin_stream_from_content_seconds : Optional [float ]
41
+ """Beginning of the stream in seconds (float or None).
42
+ This is min(frame.pts) for all frames in this stream."""
43
+ end_stream_from_content_seconds : Optional [float ]
44
+ """End of the stream in seconds (float or None).
45
+ This is max(frame.pts + frame.duration) for all frames in this stream.
46
+ Note that frames have a pts and duration and the interval defined by
47
+ [pts, pts + duration) is a half-open interval (the right boundary is open).
48
+ Therefore no frame is displayed at this time value.
49
+ Calling
50
+ SimpleVideoDecoder.get_frame_displayed_at(end_stream_from_content_seconds)
51
+ will raise a StopIteration exception.
52
+ If you want to get the last frame you can use [-1] on a SimpleVideoDecoder
53
+ object."""
43
54
codec : Optional [str ]
44
55
"""Codec (str or None)."""
45
56
width : Optional [int ]
46
57
"""Width of the frames (int or None)."""
47
58
height : Optional [int ]
48
59
"""Height of the frames (int or None)."""
49
- average_fps : Optional [float ]
60
+ average_fps_from_header : Optional [float ]
50
61
"""Averate fps of the stream (float or None)."""
51
62
stream_index : int
52
63
"""Index of the stream within the video (int)."""
@@ -62,11 +73,46 @@ def num_frames(self) -> Optional[int]:
62
73
else :
63
74
return self .num_frames_from_header
64
75
76
+ @property
77
+ def duration_seconds (self ) -> Optional [float ]:
78
+ """Duration of the stream in seconds. We try to calculate the duration
79
+ from the actual frames if we scanned the frames. Otherwise we fall back
80
+ to the duration obtained from the header.
81
+ """
82
+ if (
83
+ self .end_stream_from_content_seconds is None
84
+ or self .begin_stream_from_content_seconds is None
85
+ ):
86
+ return self .duration_seconds_from_header
87
+ return (
88
+ self .end_stream_from_content_seconds
89
+ - self .begin_stream_from_content_seconds
90
+ )
91
+
92
+ @property
93
+ def average_fps (self ) -> Optional [float ]:
94
+ """Average fps of the stream. We try to get the average fps from the
95
+ actual frames if we scanned the frames. Otherwise we fall back to the
96
+ fps obtained from the header.
97
+ """
98
+ if (
99
+ self .end_stream_from_content_seconds is None
100
+ or self .begin_stream_from_content_seconds is None
101
+ or self .num_frames is None
102
+ ):
103
+ return self .average_fps_from_header
104
+ return self .num_frames / (
105
+ self .end_stream_from_content_seconds
106
+ - self .begin_stream_from_content_seconds
107
+ )
108
+
65
109
def __repr__ (self ):
66
- # Overridden because `num_frames` wouldn't be printed by default.
110
+ # Overridden because properites are not printed by default.
67
111
s = self .__class__ .__name__ + ":\n "
68
112
spaces = " "
69
113
s += f"{ spaces } num_frames: { self .num_frames } \n "
114
+ s += f"{ spaces } duration_seconds: { self .duration_seconds } \n "
115
+ s += f"{ spaces } average_fps: { self .average_fps } \n "
70
116
for field in dataclasses .fields (self ):
71
117
s += f"{ spaces } { field .name } : { getattr (self , field .name )} \n "
72
118
return s
@@ -109,18 +155,22 @@ def get_video_metadata(decoder: torch.Tensor) -> VideoMetadata:
109
155
stream_dict = json .loads (_get_stream_json_metadata (decoder , stream_index ))
110
156
streams_metadata .append (
111
157
VideoStreamMetadata (
112
- duration_seconds = stream_dict .get ("durationSeconds" ),
158
+ duration_seconds_from_header = stream_dict .get ("durationSeconds" ),
113
159
bit_rate = stream_dict .get ("bitRate" ),
114
160
# TODO_OPEN_ISSUE: We should align the C++ names and the json
115
161
# keys with the Python names
116
162
num_frames_from_header = stream_dict .get ("numFrames" ),
117
163
num_frames_from_content = stream_dict .get ("numFramesFromScan" ),
118
- min_pts_seconds = stream_dict .get ("minPtsSecondsFromScan" ),
119
- max_pts_seconds = stream_dict .get ("maxPtsSecondsFromScan" ),
164
+ begin_stream_from_content_seconds = stream_dict .get (
165
+ "minPtsSecondsFromScan"
166
+ ),
167
+ end_stream_from_content_seconds = stream_dict .get (
168
+ "maxPtsSecondsFromScan"
169
+ ),
120
170
codec = stream_dict .get ("codec" ),
121
171
width = stream_dict .get ("width" ),
122
172
height = stream_dict .get ("height" ),
123
- average_fps = stream_dict .get ("averageFps" ),
173
+ average_fps_from_header = stream_dict .get ("averageFps" ),
124
174
stream_index = stream_index ,
125
175
)
126
176
)
0 commit comments