diff --git a/benchmarks/decoders/benchmark_decoders.py b/benchmarks/decoders/benchmark_decoders.py index 3924df450..81a8c0ea6 100644 --- a/benchmarks/decoders/benchmark_decoders.py +++ b/benchmarks/decoders/benchmark_decoders.py @@ -7,21 +7,56 @@ import argparse import importlib.resources import os +import typing +from dataclasses import dataclass, field from pathlib import Path from benchmark_decoders_library import ( - DecordNonBatchDecoderAccurateSeek, + AbstractDecoder, + DecordAccurate, + DecordAccurateBatch, plot_data, run_benchmarks, TorchAudioDecoder, TorchCodecCore, TorchCodecCoreBatch, TorchCodecCoreCompiled, + TorchCodecCoreNonBatch, TorchCodecPublic, TorchVision, ) +@dataclass +class DecoderKind: + display_name: str + kind: typing.Type[AbstractDecoder] + default_options: dict[str, str] = field(default_factory=dict) + + +decoder_registry = { + "decord": DecoderKind("DecordAccurate", DecordAccurate), + "decord_batch": DecoderKind("DecordAccurateBatch", DecordAccurateBatch), + "torchcodec_core": DecoderKind("TorchCodecCore", TorchCodecCore), + "torchcodec_core_batch": DecoderKind("TorchCodecCoreBatch", TorchCodecCoreBatch), + "torchcodec_core_nonbatch": DecoderKind( + "TorchCodecCoreNonBatch", TorchCodecCoreNonBatch + ), + "torchcodec_core_compiled": DecoderKind( + "TorchCodecCoreCompiled", TorchCodecCoreCompiled + ), + "torchcodec_public": DecoderKind("TorchCodecPublic", TorchCodecPublic), + "torchvision": DecoderKind( + # We don't compare against TorchVision's "pyav" backend because it doesn't support + # accurate seeks. + "TorchVision[backend=video_reader]", + TorchVision, + {"backend": "video_reader"}, + ), + "torchaudio": DecoderKind("TorchAudio", TorchAudioDecoder), +} + + def in_fbcode() -> bool: return "FB_PAR_RUNTIME_FILES" in os.environ @@ -35,6 +70,16 @@ def get_test_resource_path(filename: str) -> str: return str(Path(__file__).parent / f"../../test/resources/{filename}") +def parse_options_code(options_code: str) -> dict[str, str]: + options = {} + for item in options_code.split("+"): + if item.strip() == "": + continue + k, v = item.split("=") + options[k] = v + return options + + def main() -> None: """Benchmarks the performance of a few video decoders""" @@ -67,11 +112,18 @@ def main() -> None: "--decoders", help=( "Comma-separated list of decoders to benchmark. " - "Choices are torchcodec, torchaudio, torchvision, decord, tcoptions:num_threads=1+color_conversion_library=filtergraph, torchcodec_compiled" - "For torchcodec, you can specify options with tcoptions:. " + "Choices are: " + ", ".join(decoder_registry.keys()) + ". " + "To specify options, append a ':' and then value pairs seperated by a '+'. " + "For example, torchcodec_core:num_threads=1+color_conversion_library=filtergraph." ), type=str, - default="decord,tcoptions:,torchvision,torchaudio,torchcodec_compiled,torchcodec_public,tcoptions:num_threads=1,tcbatchoptions:", + default=( + "decord,decord_batch," + "torchvision," + "torchaudio," + "torchcodec_core,torchcodec_core:num_threads=1,torchcodec_core_batch,torchcodec_core_nonbatch," + "torchcodec_public" + ), ) parser.add_argument( "--bm_video_dir", @@ -87,51 +139,26 @@ def main() -> None: ) args = parser.parse_args() - decoders = set(args.decoders.split(",")) + specified_decoders = set(args.decoders.split(",")) # These are the PTS values we want to extract from the small video. num_uniform_samples = 10 - decoder_dict = {} - for decoder in decoders: - if decoder == "decord": - decoder_dict["DecordNonBatchDecoderAccurateSeek"] = ( - DecordNonBatchDecoderAccurateSeek() - ) - elif decoder == "torchcodec": - decoder_dict["TorchCodecCore:"] = TorchCodecCore() - elif decoder == "torchcodec_compiled": - decoder_dict["TorchCodecCoreCompiled"] = TorchCodecCoreCompiled() - elif decoder == "torchcodec_public": - decoder_dict["TorchCodecPublic"] = TorchCodecPublic() - elif decoder == "torchvision": - decoder_dict["TorchVision[backend=video_reader]"] = ( - # We don't compare TorchVision's "pyav" backend because it doesn't support - # accurate seeks. - TorchVision("video_reader") - ) - elif decoder == "torchaudio": - decoder_dict["TorchAudioDecoder"] = TorchAudioDecoder() - elif decoder.startswith("tcbatchoptions:"): - options = decoder[len("tcbatchoptions:") :] - kwargs_dict = {} - for item in options.split("+"): - if item.strip() == "": - continue - k, v = item.split("=") - kwargs_dict[k] = v - decoder_dict["TorchCodecCoreBatch" + options] = TorchCodecCoreBatch( - **kwargs_dict - ) - elif decoder.startswith("tcoptions:"): - options = decoder[len("tcoptions:") :] - kwargs_dict = {} - for item in options.split("+"): - if item.strip() == "": - continue - k, v = item.split("=") - kwargs_dict[k] = v - decoder_dict["TorchCodecCore:" + options] = TorchCodecCore(**kwargs_dict) + decoders_to_run = {} + for decoder in specified_decoders: + if ":" in decoder: + decoder, _, options_code = decoder.partition(":") + assert decoder in decoder_registry, f"Unknown decoder: {decoder}" + display = decoder_registry[decoder].display_name + ":" + options_code + options = parse_options_code(options_code) + else: + assert decoder in decoder_registry, f"Unknown decoder: {decoder}" + display = decoder_registry[decoder].display_name + options = decoder_registry[decoder].default_options + + kind = decoder_registry[decoder].kind + decoders_to_run[display] = kind(**options) + video_paths = args.bm_video_paths.split(",") if args.bm_video_dir: video_paths = [] @@ -140,7 +167,7 @@ def main() -> None: video_paths.append(entry.path) df_data = run_benchmarks( - decoder_dict, + decoders_to_run, video_paths, num_uniform_samples, num_sequential_frames_from_start=[1, 10, 100], diff --git a/benchmarks/decoders/benchmark_decoders_library.py b/benchmarks/decoders/benchmark_decoders_library.py index f69d709c4..f4be7d8e6 100644 --- a/benchmarks/decoders/benchmark_decoders_library.py +++ b/benchmarks/decoders/benchmark_decoders_library.py @@ -2,7 +2,6 @@ import json import os import subprocess -import timeit from concurrent.futures import ThreadPoolExecutor, wait from itertools import product @@ -18,6 +17,7 @@ _add_video_stream, create_from_file, get_frames_at_indices, + get_frames_by_pts, get_json_metadata, get_next_frame, scan_all_streams_to_update_metadata, @@ -37,47 +37,51 @@ def get_frames_from_video(self, video_file, pts_list): pass -class DecordNonBatchDecoderAccurateSeek(AbstractDecoder): +class DecordAccurate(AbstractDecoder): def __init__(self): import decord # noqa: F401 self.decord = decord - - self._print_each_iteration_time = False + self.decord.bridge.set_bridge("torch") def get_frames_from_video(self, video_file, pts_list): - self.decord.bridge.set_bridge("torch") decord_vr = self.decord.VideoReader(video_file, ctx=self.decord.cpu()) frames = [] - times = [] fps = decord_vr.get_avg_fps() for pts in pts_list: - start = timeit.default_timer() decord_vr.seek_accurate(int(pts * fps)) frame = decord_vr.next() - end = timeit.default_timer() - times.append(round(end - start, 3)) frames.append(frame) - if self._print_each_iteration_time: - print("decord times=", times, sum(times)) return frames def get_consecutive_frames_from_video(self, video_file, numFramesToDecode): - self.decord.bridge.set_bridge("torch") decord_vr = self.decord.VideoReader(video_file, ctx=self.decord.cpu()) frames = [] - times = [] for _ in range(numFramesToDecode): - start = timeit.default_timer() frame = decord_vr.next() - end = timeit.default_timer() - times.append(round(end - start, 3)) frames.append(frame) - if self._print_each_iteration_time: - print("decord times=", times, sum(times)) return frames +class DecordAccurateBatch(AbstractDecoder): + def __init__(self): + import decord # noqa: F401 + + self.decord = decord + self.decord.bridge.set_bridge("torch") + + def get_frames_from_video(self, video_file, pts_list): + decord_vr = self.decord.VideoReader(video_file, ctx=self.decord.cpu()) + average_fps = decord_vr.get_avg_fps() + indices_list = [int(pts * average_fps) for pts in pts_list] + return decord_vr.get_batch(indices_list) + + def get_consecutive_frames_from_video(self, video_file, numFramesToDecode): + decord_vr = self.decord.VideoReader(video_file, ctx=self.decord.cpu()) + indices_list = list(range(numFramesToDecode)) + return decord_vr.get_batch(indices_list) + + class TorchVision(AbstractDecoder): def __init__(self, backend): self._backend = backend @@ -87,47 +91,64 @@ def __init__(self, backend): self.torchvision = torchvision def get_frames_from_video(self, video_file, pts_list): - start = timeit.default_timer() self.torchvision.set_video_backend(self._backend) reader = self.torchvision.io.VideoReader(video_file, "video") - create_done = timeit.default_timer() frames = [] for pts in pts_list: reader.seek(pts) frame = next(reader) frames.append(frame["data"].permute(1, 2, 0)) - frames_done = timeit.default_timer() - if self._print_each_iteration_time: - create_duration = 1000 * round(create_done - start, 3) - frames_duration = 1000 * round(frames_done - create_done, 3) - total_duration = 1000 * round(frames_done - start, 3) - print(f"TV: {create_duration=} {frames_duration=} {total_duration=}") return frames def get_consecutive_frames_from_video(self, video_file, numFramesToDecode): - start = timeit.default_timer() self.torchvision.set_video_backend(self._backend) reader = self.torchvision.io.VideoReader(video_file, "video") - create_done = timeit.default_timer() frames = [] for _ in range(numFramesToDecode): frame = next(reader) frames.append(frame["data"].permute(1, 2, 0)) - frames_done = timeit.default_timer() - - if self._print_each_iteration_time: - create_duration = 1000 * round(create_done - start, 3) - frames_duration = 1000 * round(frames_done - create_done, 3) - total_duration = 1000 * round(frames_done - start, 3) - print( - f"TV: consecutive: {create_duration=} {frames_duration=} {total_duration=} {frames[0].shape=}" - ) return frames class TorchCodecCore(AbstractDecoder): def __init__(self, num_threads=None, color_conversion_library=None, device="cpu"): - self._print_each_iteration_time = False + self._num_threads = int(num_threads) if num_threads else None + self._color_conversion_library = color_conversion_library + self._device = device + + def get_frames_from_video(self, video_file, pts_list): + decoder = create_from_file(video_file) + scan_all_streams_to_update_metadata(decoder) + _add_video_stream( + decoder, + num_threads=self._num_threads, + color_conversion_library=self._color_conversion_library, + ) + metadata = json.loads(get_json_metadata(decoder)) + best_video_stream = metadata["bestVideoStreamIndex"] + frames, *_ = get_frames_by_pts( + decoder, stream_index=best_video_stream, timestamps=pts_list + ) + return frames + + def get_consecutive_frames_from_video(self, video_file, numFramesToDecode): + decoder = create_from_file(video_file) + _add_video_stream( + decoder, + num_threads=self._num_threads, + color_conversion_library=self._color_conversion_library, + ) + + frames = [] + for _ in range(numFramesToDecode): + frame = get_next_frame(decoder) + frames.append(frame) + + return frames + + +class TorchCodecCoreNonBatch(AbstractDecoder): + def __init__(self, num_threads=None, color_conversion_library=None, device="cpu"): self._num_threads = int(num_threads) if num_threads else None self._color_conversion_library = color_conversion_library self._device = device @@ -140,49 +161,28 @@ def get_frames_from_video(self, video_file, pts_list): color_conversion_library=self._color_conversion_library, device=self._device, ) + frames = [] - times = [] for pts in pts_list: - start = timeit.default_timer() seek_to_pts(decoder, pts) frame = get_next_frame(decoder) - end = timeit.default_timer() - times.append(round(end - start, 3)) frames.append(frame) - if self._print_each_iteration_time: - print("torchcodec times=", times, sum(times)) return frames def get_consecutive_frames_from_video(self, video_file, numFramesToDecode): - create_time = timeit.default_timer() decoder = create_from_file(video_file) - add_stream_time = timeit.default_timer() _add_video_stream( decoder, num_threads=self._num_threads, color_conversion_library=self._color_conversion_library, ) + frames = [] - times = [] - frames_time = timeit.default_timer() for _ in range(numFramesToDecode): - start = timeit.default_timer() frame = get_next_frame(decoder) - end = timeit.default_timer() - times.append(round(end - start, 3)) frames.append(frame) - if self._print_each_iteration_time: - done_time = timeit.default_timer() - create_duration = 1000 * round(add_stream_time - create_time, 3) - add_stream_duration = 1000 * round(frames_time - add_stream_time, 3) - frames_duration = 1000 * round(done_time - frames_time, 3) - total_duration = 1000 * round(done_time - create_time, 3) - print( - f"{numFramesToDecode=} {create_duration=} {add_stream_duration=} {frames_duration=} {total_duration=} {frames[0][0].shape=}" - ) - print("torchcodec times=", times, sum(times)) return frames @@ -201,12 +201,9 @@ def get_frames_from_video(self, video_file, pts_list): color_conversion_library=self._color_conversion_library, ) metadata = json.loads(get_json_metadata(decoder)) - average_fps = metadata["averageFps"] best_video_stream = metadata["bestVideoStreamIndex"] - indices_list = [int(pts * average_fps) for pts in pts_list] - frames = [] - frames, *_ = get_frames_at_indices( - decoder, stream_index=best_video_stream, frame_indices=indices_list + frames, *_ = get_frames_by_pts( + decoder, stream_index=best_video_stream, timestamps=pts_list ) return frames @@ -220,7 +217,6 @@ def get_consecutive_frames_from_video(self, video_file, numFramesToDecode): ) metadata = json.loads(get_json_metadata(decoder)) best_video_stream = metadata["bestVideoStreamIndex"] - frames = [] indices_list = list(range(numFramesToDecode)) frames, *_ = get_frames_at_indices( decoder, stream_index=best_video_stream, frame_indices=indices_list @@ -584,7 +580,7 @@ def run_benchmarks( "create_torchcodec_decoder_from_file": create_torchcodec_decoder_from_file, }, label=f"video={first_video_file_path} {metadata_label}", - sub_label="TorchCodecCore:", + sub_label="TorchCodecCore", description="create()+next()", ) results.append( diff --git a/benchmarks/decoders/benchmark_readme_chart.png b/benchmarks/decoders/benchmark_readme_chart.png index a1fd6b472..8c5466cc2 100644 Binary files a/benchmarks/decoders/benchmark_readme_chart.png and b/benchmarks/decoders/benchmark_readme_chart.png differ diff --git a/benchmarks/decoders/benchmark_readme_data.json b/benchmarks/decoders/benchmark_readme_data.json index 56d50fb6d..38a32c6c5 100644 --- a/benchmarks/decoders/benchmark_readme_data.json +++ b/benchmarks/decoders/benchmark_readme_data.json @@ -2,144 +2,180 @@ { "decoder": "TorchCodec", "description": "uniform 10 seek()+next()", - "fps": 315.05924655387844, - "fps_p25": 323.6567367722293, - "fps_p75": 303.4217234978234, + "fps": 312.84353293082387, + "fps_p25": 322.7871320121642, + "fps_p75": 302.8227679557008, "frame_count": 10, - "iqr": 0.0020604978781193495, - "median": 0.031740061938762665, + "iqr": 0.0020424467511475086, + "median": 0.03196486085653305, "type": "uniform:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchCodec", "description": "random 10 seek()+next()", - "fps": 312.8775910187949, - "fps_p25": 316.65411586172576, - "fps_p75": 304.67664202566044, + "fps": 313.33763162396417, + "fps_p25": 321.75500163530694, + "fps_p75": 303.2479405107031, "frame_count": 10, - "iqr": 0.0012414834462106256, - "median": 0.03196138134226203, + "iqr": 0.0018967683427035809, + "median": 0.03191445581614971, "type": "random:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchCodec", "description": "100 next()", - "fps": 948.3281062356014, - "fps_p25": 1211.2939721426944, - "fps_p75": 884.5162407819654, + "fps": 1194.7216700127474, + "fps_p25": 1256.8548260461632, + "fps_p75": 900.2557223623917, "frame_count": 100, - "iqr": 0.030499806627631187, - "median": 0.10544873587787151, + "iqr": 0.031515865586698055, + "median": 0.08370150346308947, "type": "next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchCodec[num_threads=1]", "description": "uniform 10 seek()+next()", - "fps": 131.3991915256709, - "fps_p25": 132.80657677770856, - "fps_p75": 130.0875105195256, + "fps": 130.63321225706912, + "fps_p25": 131.92649622587825, + "fps_p75": 129.0592076739796, "frame_count": 10, - "iqr": 0.0015738545916974545, - "median": 0.07610396901145577, + "iqr": 0.0016840321477502584, + "median": 0.0765502112917602, "type": "uniform:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchCodec[num_threads=1]", "description": "random 10 seek()+next()", - "fps": 131.76952597486445, - "fps_p25": 133.43827127306128, - "fps_p75": 129.72933699334547, + "fps": 131.88189359890464, + "fps_p25": 132.88939755851635, + "fps_p75": 130.466572672506, "frame_count": 10, - "iqr": 0.0021425478626042604, - "median": 0.0758900810033083, + "iqr": 0.0013974376488476992, + "median": 0.07582542020827532, "type": "random:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchCodec[num_threads=1]", "description": "100 next()", - "fps": 767.4262194030912, - "fps_p25": 954.5964481242703, - "fps_p75": 751.3156802763621, + "fps": 976.1142182056929, + "fps_p25": 1016.0800435864988, + "fps_p75": 795.6587254838081, "frame_count": 100, - "iqr": 0.028343535726889968, - "median": 0.13030568603426218, + "iqr": 0.027264581993222237, + "median": 0.10244702734053135, "type": "next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchVision[backend=video_reader]", "description": "uniform 10 seek()+next()", - "fps": 7.87802293808524, - "fps_p25": 7.9221952370727795, - "fps_p75": 7.816352619742458, + "fps": 7.77759549232789, + "fps_p25": 7.818750688897255, + "fps_p75": 7.708450468534736, "frame_count": 10, - "iqr": 0.017092708498239517, - "median": 1.2693540090695024, + "iqr": 0.018300878116860986, + "median": 1.2857444193214178, "type": "uniform:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchVision[backend=video_reader]", "description": "random 10 seek()+next()", - "fps": 7.257307771543773, - "fps_p25": 7.305370582206469, - "fps_p75": 7.209328679013935, + "fps": 7.160186815967891, + "fps_p25": 7.1908539836642165, + "fps_p75": 7.0379416903234775, "frame_count": 10, - "iqr": 0.018235752126201987, - "median": 1.3779214434325695, + "iqr": 0.030214559519663453, + "median": 1.3966116048395634, "type": "random:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchVision[backend=video_reader]", "description": "100 next()", - "fps": 843.6237746060216, - "fps_p25": 852.9895625098609, - "fps_p75": 830.8058002517034, + "fps": 842.1387064892538, + "fps_p25": 849.6200178567948, + "fps_p75": 831.8435513909374, "frame_count": 100, - "iqr": 0.0031303432770073414, - "median": 0.11853625159710646, + "iqr": 0.0025152377784252167, + "median": 0.11874528415501118, "type": "next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchAudio", "description": "uniform 10 seek()+next()", - "fps": 28.283386183212908, - "fps_p25": 28.48861769505288, - "fps_p75": 27.962052080094207, + "fps": 27.90513829975662, + "fps_p25": 28.13882301993776, + "fps_p75": 27.54551640075421, "frame_count": 10, - "iqr": 0.006610161624848843, - "median": 0.3535644542425871, + "iqr": 0.007654597284272313, + "median": 0.35835694102570415, "type": "uniform:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchAudio", "description": "random 10 seek()+next()", - "fps": 26.009247898010745, - "fps_p25": 26.13538377314808, - "fps_p75": 25.757236346453418, + "fps": 25.684434182425214, + "fps_p25": 25.879457397198443, + "fps_p75": 25.267930621911752, "frame_count": 10, - "iqr": 0.00561736966483295, - "median": 0.38447863003239036, + "iqr": 0.009351701475679874, + "median": 0.38934087194502354, "type": "random:seek()+next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, { "decoder": "TorchAudio", "description": "100 next()", - "fps": 659.7600283811723, - "fps_p25": 668.0071480761926, - "fps_p75": 652.052492189031, + "fps": 667.0015484089075, + "fps_p25": 673.7295038551645, + "fps_p75": 660.0931578201269, "frame_count": 100, - "iqr": 0.003662889124825597, - "median": 0.15157026145607233, + "iqr": 0.0030662475619465113, + "median": 0.14992468943819404, + "type": "next()", + "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" + }, + { + "decoder": "Decord", + "description": "uniform 10 seek()+next()", + "fps": 250.76314292043256, + "fps_p25": 255.79988799274403, + "fps_p75": 243.70338823712694, + "frame_count": 10, + "iqr": 0.0019404292106628418, + "median": 0.03987826872617006, + "type": "uniform:seek()+next()", + "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" + }, + { + "decoder": "Decord", + "description": "random 10 seek()+next()", + "fps": 108.95659719588485, + "fps_p25": 111.19152822111404, + "fps_p75": 106.52264411126707, + "frame_count": 10, + "iqr": 0.003941844217479229, + "median": 0.09177966509014368, + "type": "random:seek()+next()", + "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" + }, + { + "decoder": "Decord", + "description": "100 next()", + "fps": 1159.3142395946288, + "fps_p25": 1169.7791224490422, + "fps_p75": 1140.3958820854073, + "frame_count": 100, + "iqr": 0.0022026230581104755, + "median": 0.08625788986682892, "type": "next()", "video": "/tmp/torchcodec_benchmarking_videos/640x480_10s_30fps_600gop_libx264_yuv420p.mp4" }, diff --git a/benchmarks/decoders/generate_readme_data.py b/benchmarks/decoders/generate_readme_data.py index 03b47f9e8..01d30b571 100644 --- a/benchmarks/decoders/generate_readme_data.py +++ b/benchmarks/decoders/generate_readme_data.py @@ -12,6 +12,7 @@ from pathlib import Path from benchmark_decoders_library import ( + DecordAccurateBatch, generate_videos, run_benchmarks, TorchAudioDecoder, @@ -50,6 +51,7 @@ def main() -> None: decoder_dict["TorchCodec[num_threads=1]"] = TorchCodecPublic(num_ffmpeg_threads=1) decoder_dict["TorchVision[backend=video_reader]"] = TorchVision("video_reader") decoder_dict["TorchAudio"] = TorchAudioDecoder() + decoder_dict["Decord"] = DecordAccurateBatch() # These are the number of uniform seeks we do in the seek+decode benchmark. num_samples = 10