Skip to content

Commit 09799a6

Browse files
author
Daniel Flores
committed
swap video, factorize ffprobe, comments
1 parent 0ad86a4 commit 09799a6

File tree

1 file changed

+25
-19
lines changed

1 file changed

+25
-19
lines changed

examples/decoding/custom_frame_mappings.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@
2424
# %%
2525
# First, some boilerplate: we'll download a short video from the web, and
2626
# use ffmpeg to create a longer version by repeating it multiple times. We'll end up
27-
# with two videos: a short one of approximately 3 minutes and a long one of about 13 minutes.
27+
# with two videos: a short one of approximately 14 seconds and a long one of about 14 minutes.
2828
# You can ignore this part and skip below to :ref:`frame_mappings_creation`.
2929

3030
import tempfile
3131
from pathlib import Path
3232
import subprocess
3333
import requests
3434

35-
url = "https://download.pytorch.org/torchaudio/tutorial-assets/stream-api/NASAs_Most_Scientifically_Complex_Space_Observatory_Requires_Precision-MP4_small.mp4"
35+
# Video source: https://www.pexels.com/video/dog-eating-854132/
36+
# License: CC0. Author: Coverr.
37+
url = "https://videos.pexels.com/video-files/854132/854132-sd_640_360_25fps.mp4"
3638
response = requests.get(url, headers={"User-Agent": ""})
3739
if response.status_code != 200:
3840
raise RuntimeError(f"Failed to download video. {response.status_code = }.")
@@ -46,7 +48,7 @@
4648
long_video_path = Path(temp_dir) / "long_video.mp4"
4749
ffmpeg_command = [
4850
"ffmpeg",
49-
"-stream_loop", "3", # repeat video 3 times to get a ~13 min video
51+
"-stream_loop", "80", # repeat video 80 times to get a ~18 min video
5052
"-i", f"{short_video_path}",
5153
"-c", "copy",
5254
f"{long_video_path}"
@@ -77,24 +79,27 @@
7779
from time import perf_counter_ns
7880
import json
7981

82+
83+
# Lets define a simple function to run ffprobe on a video's first stream index, then writes the results in output_json_path.
84+
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"]
86+
print(f"Running ffprobe:\n{' '.join(ffprobe_cmd)}")
87+
ffprobe_result = subprocess.run(ffprobe_cmd, check=True, capture_output=True, text=True)
88+
with open(output_json_path, "w") as f:
89+
f.write(ffprobe_result.stdout)
90+
91+
8092
stream_index = 0
8193
long_json_path = Path(temp_dir) / "long_custom_frame_mappings.json"
8294
short_json_path = Path(temp_dir) / "short_custom_frame_mappings.json"
8395

84-
ffprobe_cmd = ["ffprobe", "-i", f"{long_video_path}", "-select_streams", f"{stream_index}", "-show_frames", "-show_entries", "frame=pts,duration,key_frame", "-of", "json"]
85-
ffprobe_result = subprocess.run(ffprobe_cmd, check=True, capture_output=True, text=True)
86-
with open(long_json_path, "w") as f:
87-
f.write(ffprobe_result.stdout)
88-
89-
ffprobe_cmd = ["ffprobe", "-i", f"{short_video_path}", "-select_streams", f"{stream_index}", "-show_frames", "-show_entries", "frame=pts,duration,key_frame", "-of", "json"]
90-
ffprobe_result = subprocess.run(ffprobe_cmd, check=True, capture_output=True, text=True)
91-
with open(short_json_path, "w") as f:
92-
f.write(ffprobe_result.stdout)
93-
94-
sample_data = json.loads(ffprobe_result.stdout)
95-
print("Data structure of custom frame mappings:")
96+
generate_frame_mappings(long_video_path, long_json_path, stream_index)
97+
generate_frame_mappings(short_video_path, short_json_path, stream_index)
98+
with open(short_json_path) as f:
99+
sample_data = json.loads(f.read())
100+
print("Sample of fields in custom frame mappings:")
96101
for frame in sample_data["frames"][:3]:
97-
print(f"{frame}")
102+
print(f"{frame['key_frame'] = }, {frame['pts'] = }, {frame['duration'] = }")
98103

99104
# %%
100105
# .. _custom_frame_mappings_perf_creation:
@@ -103,12 +108,13 @@
103108
# --------------------------------------
104109
#
105110
# Custom frame mappings affect the **creation** of a :class:`~torchcodec.decoders.VideoDecoder`
106-
# object. As video length increases, the performance gain compared to exact mode increases.
111+
# object. As video length or resolution increases, the performance gain compared to exact mode increases.
107112
#
108113

109114
import torch
110115

111116

117+
# Here, we define a benchmarking function, with the option to seek to the start of a file_like.
112118
def bench(f, file_like=False, average_over=50, warmup=2, **f_kwargs):
113119
for _ in range(warmup):
114120
f(**f_kwargs)
@@ -145,9 +151,9 @@ def bench(f, file_like=False, average_over=50, warmup=2, **f_kwargs):
145151
# Performance: Frame decoding with custom frame mappings
146152
# ------------------------------------------------------
147153
#
148-
# Although using custom_frame_mappings only impacts the initialization speed of
154+
# Although using ``custom_frame_mappings`` only impacts the initialization speed of
149155
# :class:`~torchcodec.decoders.VideoDecoder`, decoding workflows
150-
# usually involve creating a :class:`~torchcodec.decoders.VideoDecoder` instance,
156+
# involve creating a :class:`~torchcodec.decoders.VideoDecoder` instance,
151157
# so the performance benefits are realized.
152158

153159

0 commit comments

Comments
 (0)