Skip to content

packets

Classes:

  • ScenePacketStats

    A class representing the packet size statistics for a scene in a video.

  • VideoPackets

    A class representing video packet sizes for each frame in a video.

packets_storage module-attribute

packets_storage = PackageStorage(package_name='packets')

ScenePacketStats

Bases: TypedDict

A class representing the packet size statistics for a scene in a video.

Attributes:

PktSceneAvgSize instance-attribute

PktSceneAvgSize: float

The average packet size for the scene.

PktSceneMaxSize instance-attribute

PktSceneMaxSize: float

The maximum packet size for the scene.

PktSceneMinSize instance-attribute

PktSceneMinSize: float

The minimum packet size for the scene.

VideoPackets

Bases: list[int]

A class representing video packet sizes for each frame in a video.

Packet sizes are useful for analyzing video encoding characteristics such as bitrate, allowing you to process frames and/or scenes based on packet sizes.

Methods:

  • apply_props

    Apply packet size properties to a clip.

  • from_clip

    Obtain packet sizes from a given clip.

  • from_file

    Obtain packet sizes from a given file.

  • from_video

    Obtain packet sizes from a video file.

  • get_scenestats

    Calculate scene-based packet size statistics by referencing Keyframes.

apply_props

apply_props(
    clip: VideoNodeT,
    keyframes: Keyframes | None = None,
    *,
    func: FuncExceptT | None = None
) -> VideoNodeT

Apply packet size properties to a clip.

Parameters:

  • clip

    (VideoNodeT) –

    The clip to apply the packet size properties to.

  • keyframes

    (Keyframes | None, default: None ) –

    The keyframe list to get scene packet statistics for. If None, the packet size properties will be applied to each frame. Default: None.

  • func

    (FuncExceptT | None, default: None ) –

    An optional function to use for error handling. This should only be set by package developers.

Returns:

  • VideoNodeT

    A clip with the packet size properties applied.

Source code
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
def apply_props(
    self, clip: VideoNodeT,
    keyframes: Keyframes | None = None,
    *, func: FuncExceptT | None = None
) -> VideoNodeT:
    """
    Apply packet size properties to a clip.

    :param clip:        The clip to apply the packet size properties to.
    :param keyframes:   The keyframe list to get scene packet statistics for.
                        If None, the packet size properties will be applied to each frame.
                        Default: None.
    :param func:        An optional function to use for error handling.
                        This should only be set by package developers.

    :return:            A clip with the packet size properties applied.
    """

    func = func or self.apply_props

    def _set_sizes_props(n: int) -> vs.VideoNode:
        if (pkt_size := self[n]) < 0:
            warnings.warn(f'{func}: \'Frame {n} bitrate could not be determined!\'')

        return clip.std.SetFrameProps(PktSize=pkt_size)

    if not keyframes:
        return vs.core.std.FrameEval(clip, _set_sizes_props)

    def _set_scene_stats(n: int, keyframes: Keyframes) -> vs.VideoNode:
        if (pkt_size := self[n]) < 0:
            warnings.warn(f'{func}: \'Frame {n} bitrate could not be determined!\'')

        try:
            return clip.std.SetFrameProps(PktSize=pkt_size, **scenestats[keyframes.scenes.indices[n]])
        except Exception:
            warnings.warn(f'{func}: \'Could not find stats for a section... (Frame: {n})\'')

            return clip.std.SetFrameProps(
                PktSize=-1,
                PktSceneAvgSize=-1,
                PktSceneMaxSize=-1,
                PktSceneMinSize=-1
            )

    scenestats = self.get_scenestats(keyframes)

    return vs.core.std.FrameEval(clip, lambda n: _set_scene_stats(n, keyframes))

from_clip classmethod

from_clip(
    clip: VideoNode,
    out_file: SPathLike,
    src_file: SPathLike | None = None,
    offset: int = 0,
    *,
    func: FuncExceptT | None = None
) -> Self

Obtain packet sizes from a given clip.

Parameters:

  • clip

    (VideoNode) –

    The clip to obtain packet sizes from. Must have the IdxFilePath frame property.

  • out_file

    (SPathLike) –

    The path to the output file where packet sizes will be saved.

  • src_file

    (SPathLike | None, default: None ) –

    The path to the source video file. If None, the source file will be obtained from the clip. Default: None.

Source code
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
@classmethod
def from_clip(
    cls, clip: vs.VideoNode,
    out_file: SPathLike, src_file: SPathLike | None = None,
    offset: int = 0, *, func: FuncExceptT | None = None
) -> Self:
    """
    Obtain packet sizes from a given clip.

    :param clip:        The clip to obtain packet sizes from.
                        Must have the `IdxFilePath` frame property.
    :param out_file:    The path to the output file where packet sizes will be saved.
    :param src_file:    The path to the source video file.
                        If None, the source file will be obtained from the clip.
                        Default: None.
    """

    from ..utils import get_clip_filepath

    func = func or cls.from_video

    out_file = SPath(str(out_file)).stem + f'_{clip.num_frames}_{clip.fps_num}_{clip.fps_den}'

    if video_packets := cls.from_file(out_file, func=func):
        return video_packets

    if (src_file := get_clip_filepath(clip, src_file, func=func)) is None:
        raise CustomValueError('You must provide a source file!', func)

    return cls.from_video(src_file, out_file, offset, func=func)

from_file classmethod

from_file(file: SPathLike, *, func: FuncExceptT | None = None) -> Self | None

Obtain packet sizes from a given file.

Parameters:

  • file

    (SPathLike) –

    The path to the file containing the packet sizes.

  • func

    (FuncExceptT | None, default: None ) –

    An optional function to use for error handling. This should only be set by package developers.

Returns:

  • Self | None

    A VideoPackets object containing the packet sizes.

Source code
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
@classmethod
def from_file(cls, file: SPathLike, *, func: FuncExceptT | None = None) -> Self | None:
    """
    Obtain packet sizes from a given file.

    :param file:    The path to the file containing the packet sizes.
    :param func:    An optional function to use for error handling.
                    This should only be set by package developers.

    :return:        A VideoPackets object containing the packet sizes.
    """

    if file is not None:
        file = packets_storage.get_file(file, ext='.txt')

        if file.exists() and not file.stat().st_size:
            file.unlink()

    if file is not None and file.exists():
        with file.open('r+') as f:
            return cls(map(int, f.readlines()))

    return None

from_video classmethod

from_video(
    src_file: SPathLike,
    out_file: SPathLike | None = None,
    offset: int = 0,
    *,
    func: FuncExceptT | None = None
) -> Self

Obtain packet sizes from a video file.

If the packet sizes are already calculated, they will be read from the output file. Otherwise, this method will use ffprobe to calculate the packet sizes and save them to the output file.

offset can be used to remove or add frames from the start of the list. This is useful for applying the packet sizes to a trimmed clip. Positive values will trim the start of the list, and negative values will duplicate packets at the start of the list.

Parameters:

  • src_file

    (SPathLike) –

    The path to the source video file.

  • out_file

    (SPathLike | None, default: None ) –

    The path to the output file where packet sizes will be saved. If None, output file will be placed alongside the source file. Default: None.

  • offset

    (int, default: 0 ) –

    An optional integer offset to trim the packet sizes. This is useful for applying the packet sizes to a trimmed clip. Positive values will trim the start of the list, and negative values will duplicate packets at the start of the list. Default: 0.

  • func

    (FuncExceptT | None, default: None ) –

    An optional function to use for error handling. This should only be set by package developers.

Returns:

  • Self

    A VideoPackets object containing the packet sizes.

Source code
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@classmethod
def from_video(
    cls, src_file: SPathLike, out_file: SPathLike | None = None,
    offset: int = 0, *, func: FuncExceptT | None = None
) -> Self:
    """
    Obtain packet sizes from a video file.

    If the packet sizes are already calculated, they will be read from the output file.
    Otherwise, this method will use `ffprobe` to calculate the packet sizes and save them to the output file.

    `offset` can be used to remove or add frames from the start of the list. This is useful for applying
    the packet sizes to a trimmed clip. Positive values will trim the start of the list, and negative values
    will duplicate packets at the start of the list.

    :param src_file:        The path to the source video file.
    :param out_file:        The path to the output file where packet sizes will be saved.
                            If None, output file will be placed alongside the source file.
                            Default: None.
    :param offset:          An optional integer offset to trim the packet sizes.
                            This is useful for applying the packet sizes to a trimmed clip.
                            Positive values will trim the start of the list, and negative values
                            will duplicate packets at the start of the list.
                            Default: 0.
    :param func:            An optional function to use for error handling.
                            This should only be set by package developers.

    :return:                A VideoPackets object containing the packet sizes.
    """

    func = func or cls.from_video

    src_file = SPath(src_file)

    if not src_file.exists():
        raise CustomValueError('Source file not found!', func, src_file.absolute())

    if out_file is None:
        out_file = src_file.with_stem(src_file.stem + '_packets').with_suffix('.txt')

    if video_packets := cls.from_file(out_file, func=func):
        return video_packets

    out_file = packets_storage.get_file(out_file, ext='.txt')

    if not shutil.which('ffprobe'):
        raise DependencyNotFoundError(func, 'ffprobe', 'Could not find {package}! Make sure it\'s in your PATH!')

    proc = Popen([
        'ffprobe', '-hide_banner', '-show_frames', '-show_streams', '-threads', str(vs.core.num_threads),
        '-loglevel', 'quiet', '-print_format', 'json', '-select_streams', 'v:0', src_file.to_str()
    ], stdout=PIPE)

    with NamedTemporaryFile('a+', delete=False) as tempfile:
        assert proc.stdout

        for line in io.TextIOWrapper(proc.stdout, 'utf-8'):
            tempfile.write(line)

        tempfile.flush()

    try:
        with open(tempfile.name, 'r') as f:
            data = dict(json.load(f))
    finally:
        SPath(tempfile.name).unlink()

    if not (frames := data.get('frames', {})):
        raise CustomValueError(f'No frames found in file, \'{src_file}\'! Your file may be corrupted!', func)

    pkt_sizes = [int(dict(frame).get('pkt_size', -1)) for frame in frames]

    print(f'Writing packet sizes to \'{out_file.absolute()}\'...')

    out_file.write_text('\n'.join(map(str, pkt_sizes)), 'utf-8', newline='\n')

    if offset < 0:
        pkt_sizes = [-1] * -offset + pkt_sizes
    elif offset > 0:
        pkt_sizes = pkt_sizes[offset:]

    return cls(pkt_sizes)

get_scenestats

Calculate scene-based packet size statistics by referencing Keyframes.

Parameters:

  • keyframes

    (Keyframes) –

    The keyframe list to get scene packet statistics for.

Returns:

Source code
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def get_scenestats(self, keyframes: Keyframes) -> list[ScenePacketStats]:
    """
    Calculate scene-based packet size statistics by referencing Keyframes.

    :param keyframes:   The keyframe list to get scene packet statistics for.

    :return:            A list of ScenePacketStats objects.
    """

    stats = list[ScenePacketStats]()

    try:
        for start, end in zip(keyframes, keyframes[1:]):
            pkt_scenes = self[start:end]

            stats.append(
                ScenePacketStats(
                    PktSceneAvgSize=sum(pkt_scenes) / len(pkt_scenes),
                    PktSceneMaxSize=max(pkt_scenes),
                    PktSceneMinSize=min(pkt_scenes)
                )
            )
    except ValueError as e:
        raise CustomValueError('Some kind of error occurred!', self.get_scenestats, str(e))

    return stats