Skip to content

Commit 38e989e

Browse files
committed
Update Python type annotations to modern syntax and types
1 parent 4e412b7 commit 38e989e

23 files changed

+352
-328
lines changed

examples/decoding/approximate_mode.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,25 @@
4242

4343
temp_dir = tempfile.mkdtemp()
4444
short_video_path = Path(temp_dir) / "short_video.mp4"
45-
with open(short_video_path, 'wb') as f:
45+
with open(short_video_path, "wb") as f:
4646
for chunk in response.iter_content():
4747
f.write(chunk)
4848

4949
long_video_path = Path(temp_dir) / "long_video.mp4"
5050
ffmpeg_command = [
5151
"ffmpeg",
52-
"-stream_loop", "99", # repeat video 100 times
53-
"-i", f"{short_video_path}",
54-
"-c", "copy",
55-
f"{long_video_path}"
52+
"-stream_loop",
53+
"99", # repeat video 100 times
54+
"-i",
55+
f"{short_video_path}",
56+
"-c",
57+
"copy",
58+
f"{long_video_path}",
5659
]
5760
subprocess.run(ffmpeg_command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
5861

5962
from torchcodec.decoders import VideoDecoder
63+
6064
print(f"Short video duration: {VideoDecoder(short_video_path).metadata.duration_seconds} seconds")
6165
print(f"Long video duration: {VideoDecoder(long_video_path).metadata.duration_seconds / 60} minutes")
6266

@@ -116,10 +120,7 @@ def bench(f, average_over=50, warmup=2, **f_kwargs):
116120

117121
def sample_clips(seek_mode):
118122
return samplers.clips_at_random_indices(
119-
decoder=VideoDecoder(
120-
source=long_video_path,
121-
seek_mode=seek_mode
122-
),
123+
decoder=VideoDecoder(source=long_video_path, seek_mode=seek_mode),
123124
num_clips=5,
124125
num_frames_per_clip=2,
125126
)
@@ -154,7 +155,8 @@ def sample_clips(seek_mode):
154155
torch.testing.assert_close(
155156
exact_decoder.get_frame_at(i).data,
156157
approx_decoder.get_frame_at(i).data,
157-
atol=0, rtol=0,
158+
atol=0,
159+
rtol=0,
158160
)
159161
print("Frame seeking is the same for this video!")
160162

examples/decoding/basic_cuda_example.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,7 @@ def plot_cpu_and_cuda_frames(cpu_frames: torch.Tensor, cuda_frames: torch.Tensor
154154
# differences because CUDA math is not bit-exact with respect to CPU math.
155155
#
156156
frames_equal = torch.equal(cpu_frames.to("cuda"), cuda_frames)
157-
mean_abs_diff = torch.mean(
158-
torch.abs(cpu_frames.float().to("cuda") - cuda_frames.float())
159-
)
157+
mean_abs_diff = torch.mean(torch.abs(cpu_frames.float().to("cuda") - cuda_frames.float()))
160158
max_abs_diff = torch.max(torch.abs(cpu_frames.to("cuda").float() - cuda_frames.float()))
161159
print(f"{frames_equal=}")
162160
print(f"{mean_abs_diff=}")

examples/decoding/basic_example.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
:class:`~torchcodec.decoders.VideoDecoder` class.
1414
"""
1515

16+
from __future__ import annotations
17+
1618
# %%
1719
# First, a bit of boilerplate: we'll download a video from the web, and define a
1820
# plotting utility. You can ignore that part and jump right below to
1921
# :ref:`creating_decoder`.
2022

21-
from typing import Optional
2223
import torch
2324
import requests
2425

@@ -33,7 +34,7 @@
3334
raw_video_bytes = response.content
3435

3536

36-
def plot(frames: torch.Tensor, title : Optional[str] = None):
37+
def plot(frames: torch.Tensor, title: str | None = None):
3738
try:
3839
from torchvision.utils import make_grid
3940
from torchvision.transforms.v2.functional import to_pil_image
@@ -42,7 +43,7 @@ def plot(frames: torch.Tensor, title : Optional[str] = None):
4243
print("Cannot plot, please run `pip install torchvision matplotlib`")
4344
return
4445

45-
plt.rcParams["savefig.bbox"] = 'tight'
46+
plt.rcParams["savefig.bbox"] = "tight"
4647
fig, ax = plt.subplots()
4748
ax.imshow(to_pil_image(make_grid(frames)))
4849
ax.set(xticklabels=[], yticklabels=[], xticks=[], yticks=[])
@@ -76,7 +77,7 @@ def plot(frames: torch.Tensor, title : Optional[str] = None):
7677
# ---------------------------------------
7778

7879
first_frame = decoder[0] # using a single int index
79-
every_twenty_frame = decoder[0 : -1 : 20] # using slices
80+
every_twenty_frame = decoder[0:-1:20] # using slices
8081

8182
print(f"{first_frame.shape = }")
8283
print(f"{first_frame.dtype = }")
@@ -116,10 +117,7 @@ def plot(frames: torch.Tensor, title : Optional[str] = None):
116117
# The decoder is a normal iterable object and can be iterated over like so:
117118

118119
for frame in decoder:
119-
assert (
120-
isinstance(frame, torch.Tensor)
121-
and frame.shape == (3, decoder.metadata.height, decoder.metadata.width)
122-
)
120+
assert isinstance(frame, torch.Tensor) and frame.shape == (3, decoder.metadata.height, decoder.metadata.width)
123121

124122
# %%
125123
# Retrieving pts and duration of frames

examples/decoding/custom_frame_mappings.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,25 @@
4141

4242
temp_dir = tempfile.mkdtemp()
4343
short_video_path = Path(temp_dir) / "short_video.mp4"
44-
with open(short_video_path, 'wb') as f:
44+
with open(short_video_path, "wb") as f:
4545
for chunk in response.iter_content():
4646
f.write(chunk)
4747

4848
long_video_path = Path(temp_dir) / "long_video.mp4"
4949
ffmpeg_command = [
5050
"ffmpeg",
51-
"-stream_loop", "50", # repeat video 50 times to get a ~12 min video
52-
"-i", f"{short_video_path}",
53-
"-c", "copy",
54-
f"{long_video_path}"
51+
"-stream_loop",
52+
"50", # repeat video 50 times to get a ~12 min video
53+
"-i",
54+
f"{short_video_path}",
55+
"-c",
56+
"copy",
57+
f"{long_video_path}",
5558
]
5659
subprocess.run(ffmpeg_command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
5760

5861
from torchcodec.decoders import VideoDecoder
62+
5963
print(f"Short video duration: {VideoDecoder(short_video_path).metadata.duration_seconds} seconds")
6064
print(f"Long video duration: {VideoDecoder(long_video_path).metadata.duration_seconds / 60} minutes")
6165

@@ -82,7 +86,18 @@
8286

8387
# Lets define a simple function to run ffprobe on a video's first stream index, then writes the results in output_json_path.
8488
def generate_frame_mappings(video_path, output_json_path, stream_index):
85-
ffprobe_cmd = ["ffprobe", "-i", f"{video_path}", "-select_streams", f"{stream_index}", "-show_frames", "-show_entries", "frame=pts,duration,key_frame", "-of", "json"]
89+
ffprobe_cmd = [
90+
"ffprobe",
91+
"-i",
92+
f"{video_path}",
93+
"-select_streams",
94+
f"{stream_index}",
95+
"-show_frames",
96+
"-show_entries",
97+
"frame=pts,duration,key_frame",
98+
"-of",
99+
"json",
100+
]
86101
print(f"Running ffprobe:\n{' '.join(ffprobe_cmd)}\n")
87102
ffprobe_result = subprocess.run(ffprobe_cmd, check=True, capture_output=True, text=True)
88103
with open(output_json_path, "w") as f:
@@ -157,12 +172,8 @@ def bench(f, file_like=False, average_over=50, warmup=2, **f_kwargs):
157172
# so the performance benefits are realized.
158173

159174

160-
def decode_frames(video_path, seek_mode = "exact", custom_frame_mappings = None):
161-
decoder = VideoDecoder(
162-
source=video_path,
163-
seek_mode=seek_mode,
164-
custom_frame_mappings=custom_frame_mappings
165-
)
175+
def decode_frames(video_path, seek_mode="exact", custom_frame_mappings=None):
176+
decoder = VideoDecoder(source=video_path, seek_mode=seek_mode, custom_frame_mappings=custom_frame_mappings)
166177
decoder.get_frames_in_range(start=0, stop=10)
167178

168179

@@ -196,7 +207,8 @@ def decode_frames(video_path, seek_mode = "exact", custom_frame_mappings = None)
196207
torch.testing.assert_close(
197208
exact_decoder.get_frame_at(i).data,
198209
custom_frame_mappings_decoder.get_frame_at(i).data,
199-
atol=0, rtol=0,
210+
atol=0,
211+
rtol=0,
200212
)
201213
print("Frame seeking is the same for this video!")
202214

examples/decoding/file_like.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def stream_while_decode():
158158
# session; we need to indicate that we need to trust the environment
159159
# settings for proxy configuration. Depending on your environment, you may
160160
# not need this setting.
161-
with fsspec.open(nasa_url, client_kwargs={'trust_env': True}) as file_like:
161+
with fsspec.open(nasa_url, client_kwargs={"trust_env": True}) as file_like:
162162
decoder = VideoDecoder(file_like, seek_mode="approximate")
163163
return decoder[0]
164164

@@ -226,18 +226,20 @@ def seek(self, offset: int, whence: int) -> int:
226226
file_op_counter = FileOpCounter(open(nasa_video_path, "rb"))
227227
counter_decoder = VideoDecoder(file_op_counter, seek_mode="approximate")
228228

229-
print("Decoder initialization required "
230-
f"{file_op_counter.num_reads} reads and "
231-
f"{file_op_counter.num_seeks} seeks.")
229+
print(
230+
"Decoder initialization required " f"{file_op_counter.num_reads} reads and " f"{file_op_counter.num_seeks} seeks."
231+
)
232232

233233
init_reads = file_op_counter.num_reads
234234
init_seeks = file_op_counter.num_seeks
235235

236236
first_frame = counter_decoder[0]
237237

238-
print("Decoding the first frame required "
239-
f"{file_op_counter.num_reads - init_reads} additional reads and "
240-
f"{file_op_counter.num_seeks - init_seeks} additional seeks.")
238+
print(
239+
"Decoding the first frame required "
240+
f"{file_op_counter.num_reads - init_reads} additional reads and "
241+
f"{file_op_counter.num_seeks - init_seeks} additional seeks."
242+
)
241243

242244
# %%
243245
# While we defined a simple class primarily for demonstration, it's actually
@@ -299,5 +301,6 @@ def decode_from_existing_open_file_object():
299301
# %%
300302
# Finally, let's clean up the local resources we created.
301303
import shutil
304+
302305
shutil.rmtree(temp_dir)
303306
# %%

examples/decoding/parallel_decoding.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
pool manager.
2525
"""
2626

27+
from __future__ import annotations
28+
2729
# %%
2830
# Let's first define some utility functions for benchmarking and data
2931
# processing. We'll also download a video and create a longer version by
3032
# repeating it multiple times. This simulates working with long videos that
3133
# require efficient processing. You can ignore that part and jump right below to
3234
# :ref:`start_parallel_decoding`.
3335

34-
from typing import List
3536
import torch
3637
import requests
3738
import tempfile
@@ -74,16 +75,16 @@ def report_stats(times, unit="s"):
7475
return med
7576

7677

77-
def split_indices(indices: List[int], num_chunks: int) -> List[List[int]]:
78+
def split_indices(indices: list[int], num_chunks: int) -> list[list[int]]:
7879
"""Split a list of indices into approximately equal chunks."""
7980
chunk_size = len(indices) // num_chunks
8081
chunks = []
8182

8283
for i in range(num_chunks - 1):
83-
chunks.append(indices[i * chunk_size:(i + 1) * chunk_size])
84+
chunks.append(indices[i * chunk_size : (i + 1) * chunk_size])
8485

8586
# Last chunk may be slightly larger
86-
chunks.append(indices[(num_chunks - 1) * chunk_size:])
87+
chunks.append(indices[(num_chunks - 1) * chunk_size :])
8788
return chunks
8889

8990

@@ -96,18 +97,22 @@ def generate_long_video(temp_dir: str):
9697
raise RuntimeError(f"Failed to download video. {response.status_code = }.")
9798

9899
short_video_path = Path(temp_dir) / "short_video.mp4"
99-
with open(short_video_path, 'wb') as f:
100+
with open(short_video_path, "wb") as f:
100101
for chunk in response.iter_content():
101102
f.write(chunk)
102103

103104
# Create a longer video by repeating the short one 50 times
104105
long_video_path = Path(temp_dir) / "long_video.mp4"
105106
ffmpeg_command = [
106-
"ffmpeg", "-y",
107-
"-stream_loop", "49", # repeat video 50 times
108-
"-i", str(short_video_path),
109-
"-c", "copy",
110-
str(long_video_path)
107+
"ffmpeg",
108+
"-y",
109+
"-stream_loop",
110+
"49", # repeat video 50 times
111+
"-i",
112+
str(short_video_path),
113+
"-c",
114+
"copy",
115+
str(long_video_path),
111116
]
112117
subprocess.run(ffmpeg_command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
113118

@@ -122,7 +127,9 @@ def generate_long_video(temp_dir: str):
122127

123128
short_duration = timedelta(seconds=VideoDecoder(short_video_path).metadata.duration_seconds)
124129
long_duration = timedelta(seconds=metadata.duration_seconds)
125-
print(f"Original video duration: {int(short_duration.total_seconds() // 60)}m{int(short_duration.total_seconds() % 60):02d}s")
130+
print(
131+
f"Original video duration: {int(short_duration.total_seconds() // 60)}m{int(short_duration.total_seconds() % 60):02d}s"
132+
)
126133
print(f"Long video duration: {int(long_duration.total_seconds() // 60)}m{int(long_duration.total_seconds() % 60):02d}s")
127134
print(f"Video resolution: {metadata.width}x{metadata.height}")
128135
print(f"Average FPS: {metadata.average_fps:.1f}")
@@ -155,7 +162,8 @@ def generate_long_video(temp_dir: str):
155162
# Let's start with a sequential approach as our baseline. This processes
156163
# frames one by one without any parallelization.
157164

158-
def decode_sequentially(indices: List[int], video_path=long_video_path):
165+
166+
def decode_sequentially(indices: list[int], video_path=long_video_path):
159167
"""Decode frames sequentially using a single decoder instance."""
160168
decoder = VideoDecoder(video_path, seek_mode="approximate")
161169
return decoder.get_frames_at(indices)
@@ -173,11 +181,8 @@ def decode_sequentially(indices: List[int], video_path=long_video_path):
173181
# via the ``num_ffmpeg_threads`` parameter. This approach uses multiple
174182
# threads within FFmpeg itself to accelerate decoding operations.
175183

176-
def decode_with_ffmpeg_parallelism(
177-
indices: List[int],
178-
num_threads: int,
179-
video_path=long_video_path
180-
):
184+
185+
def decode_with_ffmpeg_parallelism(indices: list[int], num_threads: int, video_path=long_video_path):
181186
"""Decode frames using FFmpeg's internal threading."""
182187
decoder = VideoDecoder(video_path, num_ffmpeg_threads=num_threads, seek_mode="approximate")
183188
return decoder.get_frames_at(indices)
@@ -197,11 +202,8 @@ def decode_with_ffmpeg_parallelism(
197202
#
198203
# Process-based parallelism distributes work across multiple Python processes.
199204

200-
def decode_with_multiprocessing(
201-
indices: List[int],
202-
num_processes: int,
203-
video_path=long_video_path
204-
):
205+
206+
def decode_with_multiprocessing(indices: list[int], num_processes: int, video_path=long_video_path):
205207
"""Decode frames using multiple processes with joblib."""
206208
chunks = split_indices(indices, num_chunks=num_processes)
207209

@@ -226,11 +228,8 @@ def decode_with_multiprocessing(
226228
# Thread-based parallelism uses multiple threads within a single process.
227229
# TorchCodec releases the GIL, so this can be very effective.
228230

229-
def decode_with_multithreading(
230-
indices: List[int],
231-
num_threads: int,
232-
video_path=long_video_path
233-
):
231+
232+
def decode_with_multithreading(indices: list[int], num_threads: int, video_path=long_video_path):
234233
"""Decode frames using multiple threads with joblib."""
235234
chunks = split_indices(indices, num_chunks=num_threads)
236235

@@ -261,4 +260,5 @@ def decode_with_multithreading(
261260

262261
# %%
263262
import shutil
263+
264264
shutil.rmtree(temp_dir)

0 commit comments

Comments
 (0)