44from typing import Optional , Union , IO
55import io
66import av
7- from .._util import VideoContainer , VideoCodec , VideoComponents
7+ from .._util import VideoContainer , VideoCodec , VideoComponents , normalize_crop_rect
88
99class VideoInput (ABC ):
1010 """
@@ -31,12 +31,15 @@ def save_to(
3131 bit_depth : int | None = None ,
3232 crf : float | None = None ,
3333 color_space : str | None = None ,
34+ preset : str | None = None ,
3435 ):
3536 """
3637 Abstract method to save the video input to a file.
3738
3839 bit_depth selects the encoded bit depth; None keeps the video's native depth.
3940 crf selects the H.264 or AV1 constant rate factor; None uses the encoder default.
41+ preset selects the H.264 encoder speed/compression trade-off (e.g. "ultrafast");
42+ None uses the encoder default. Ignored for other codecs.
4043 color_space="sRGB" selects SDR BT.709/sRGB, "HDR" selects BT.2020/HLG, and "HDR PQ"
4144 selects BT.2020/PQ. Bit depth is selected independently.
4245 Tensor-created videos default to sRGB when color_space is None. Loaded videos keep matching recognized native color
@@ -63,6 +66,45 @@ def as_trimmed(
6366 """
6467 pass
6568
69+ def as_cropped (
70+ self ,
71+ x : int = 0 ,
72+ y : int = 0 ,
73+ width : int = 0 ,
74+ height : int = 0 ,
75+ ) -> VideoInput :
76+ """
77+ Create a new VideoInput spatially cropped to the given pixel rectangle.
78+
79+ The rectangle is clamped to the frame and even-aligned for encoder
80+ compatibility. An empty or full-frame rectangle returns the input
81+ unchanged.
82+
83+ Default implementation materializes the video via get_components();
84+ subclasses should override with lazier strategies when possible.
85+ """
86+ components = self .get_components ()
87+ rect = normalize_crop_rect (
88+ x , y , width , height , components .images .shape [2 ], components .images .shape [1 ]
89+ )
90+ if rect is None :
91+ return self
92+ from .._input_impl .video_types import VideoFromComponents
93+
94+ cx , cy , cw , ch = rect
95+ return VideoFromComponents (
96+ VideoComponents (
97+ images = components .images [:, cy :cy + ch , cx :cx + cw , :].clone (),
98+ audio = components .audio ,
99+ frame_rate = components .frame_rate ,
100+ metadata = components .metadata ,
101+ alpha = components .alpha [:, cy :cy + ch , cx :cx + cw ].clone ()
102+ if components .alpha is not None
103+ else None ,
104+ ),
105+ bit_depth = self .get_bit_depth (),
106+ )
107+
66108 def get_stream_source (self ) -> Union [str , io .BytesIO ]:
67109 """
68110 Get a streamable source for the video. This allows processing without
0 commit comments