torchcodec 0.3.0__cp313-cp313-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of torchcodec might be problematic. Click here for more details.

Files changed (57) hide show
  1. torchcodec/__init__.py +16 -0
  2. torchcodec/_core/AVIOBytesContext.cpp +70 -0
  3. torchcodec/_core/AVIOBytesContext.h +32 -0
  4. torchcodec/_core/AVIOContextHolder.cpp +50 -0
  5. torchcodec/_core/AVIOContextHolder.h +65 -0
  6. torchcodec/_core/AVIOFileLikeContext.cpp +80 -0
  7. torchcodec/_core/AVIOFileLikeContext.h +54 -0
  8. torchcodec/_core/CMakeLists.txt +237 -0
  9. torchcodec/_core/CudaDeviceInterface.cpp +289 -0
  10. torchcodec/_core/CudaDeviceInterface.h +34 -0
  11. torchcodec/_core/DeviceInterface.cpp +88 -0
  12. torchcodec/_core/DeviceInterface.h +66 -0
  13. torchcodec/_core/Encoder.cpp +319 -0
  14. torchcodec/_core/Encoder.h +39 -0
  15. torchcodec/_core/FFMPEGCommon.cpp +264 -0
  16. torchcodec/_core/FFMPEGCommon.h +180 -0
  17. torchcodec/_core/Frame.h +47 -0
  18. torchcodec/_core/Metadata.h +70 -0
  19. torchcodec/_core/SingleStreamDecoder.cpp +1947 -0
  20. torchcodec/_core/SingleStreamDecoder.h +462 -0
  21. torchcodec/_core/StreamOptions.h +49 -0
  22. torchcodec/_core/__init__.py +39 -0
  23. torchcodec/_core/_metadata.py +277 -0
  24. torchcodec/_core/custom_ops.cpp +681 -0
  25. torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +226 -0
  26. torchcodec/_core/ops.py +381 -0
  27. torchcodec/_core/pybind_ops.cpp +45 -0
  28. torchcodec/_frame.py +145 -0
  29. torchcodec/_internally_replaced_utils.py +53 -0
  30. torchcodec/_samplers/__init__.py +7 -0
  31. torchcodec/_samplers/video_clip_sampler.py +430 -0
  32. torchcodec/decoders/__init__.py +11 -0
  33. torchcodec/decoders/_audio_decoder.py +168 -0
  34. torchcodec/decoders/_decoder_utils.py +52 -0
  35. torchcodec/decoders/_video_decoder.py +399 -0
  36. torchcodec/libtorchcodec_custom_ops4.so +0 -0
  37. torchcodec/libtorchcodec_custom_ops5.so +0 -0
  38. torchcodec/libtorchcodec_custom_ops6.so +0 -0
  39. torchcodec/libtorchcodec_custom_ops7.so +0 -0
  40. torchcodec/libtorchcodec_decoder4.so +0 -0
  41. torchcodec/libtorchcodec_decoder5.so +0 -0
  42. torchcodec/libtorchcodec_decoder6.so +0 -0
  43. torchcodec/libtorchcodec_decoder7.so +0 -0
  44. torchcodec/libtorchcodec_pybind_ops4.so +0 -0
  45. torchcodec/libtorchcodec_pybind_ops5.so +0 -0
  46. torchcodec/libtorchcodec_pybind_ops6.so +0 -0
  47. torchcodec/libtorchcodec_pybind_ops7.so +0 -0
  48. torchcodec/samplers/__init__.py +2 -0
  49. torchcodec/samplers/_common.py +84 -0
  50. torchcodec/samplers/_index_based.py +285 -0
  51. torchcodec/samplers/_time_based.py +348 -0
  52. torchcodec/version.py +2 -0
  53. torchcodec-0.3.0.dist-info/LICENSE +28 -0
  54. torchcodec-0.3.0.dist-info/METADATA +280 -0
  55. torchcodec-0.3.0.dist-info/RECORD +57 -0
  56. torchcodec-0.3.0.dist-info/WHEEL +5 -0
  57. torchcodec-0.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,226 @@
1
+ # This file fetches the non-GPL ffmpeg libraries from the torchcodec S3 bucket,
2
+ # and exposes them as CMake targets so we can dynamically link against them.
3
+ # These libraries were built on the CI via the build_ffmpeg.yaml workflow.
4
+
5
+ # Avoid warning: see https://cmake.org/cmake/help/latest/policy/CMP0135.html
6
+ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
7
+ cmake_policy(SET CMP0135 NEW)
8
+ endif()
9
+
10
+ include(FetchContent)
11
+
12
+ set(
13
+ base_url
14
+ https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2025-03-14
15
+ )
16
+
17
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
18
+ set(
19
+ platform_url
20
+ ${base_url}/linux_x86_64
21
+ )
22
+
23
+ set(
24
+ f4_sha256
25
+ 1a083f1922443bedb5243d04896383b8c606778a7ddb9d886c8303e55339fe0c
26
+ )
27
+ set(
28
+ f5_sha256
29
+ 65d6ad54082d94dcb3f801d73df2265e0e1bb303c7afbce7723e3b77ccd0e207
30
+ )
31
+ set(
32
+ f6_sha256
33
+ 8bd5939c2f4a4b072e837e7870c13fe7d13824e5ff087ab534e4db4e90b7be9c
34
+ )
35
+ set(
36
+ f7_sha256
37
+ 1cb946d8b7c6393c2c3ebe1f900b8de7a2885fe614c45d4ec32c9833084f2f26
38
+ )
39
+
40
+ set(
41
+ f4_library_file_names
42
+ libavutil.so.56
43
+ libavcodec.so.58
44
+ libavformat.so.58
45
+ libavdevice.so.58
46
+ libavfilter.so.7
47
+ libswscale.so.5
48
+ libswresample.so.3
49
+ )
50
+ set(
51
+ f5_library_file_names
52
+ libavutil.so.57
53
+ libavcodec.so.59
54
+ libavformat.so.59
55
+ libavdevice.so.59
56
+ libavfilter.so.8
57
+ libswscale.so.6
58
+ libswresample.so.4
59
+ )
60
+ set(
61
+ f6_library_file_names
62
+ libavutil.so.58
63
+ libavcodec.so.60
64
+ libavformat.so.60
65
+ libavdevice.so.60
66
+ libavfilter.so.9
67
+ libswscale.so.7
68
+ libswresample.so.4
69
+ )
70
+ set(
71
+ f7_library_file_names
72
+ libavutil.so.59
73
+ libavcodec.so.61
74
+ libavformat.so.61
75
+ libavdevice.so.61
76
+ libavfilter.so.10
77
+ libswscale.so.8
78
+ libswresample.so.5
79
+ )
80
+ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
81
+ set(
82
+ platform_url
83
+ ${base_url}/macos_arm64
84
+ )
85
+ set(
86
+ f4_sha256
87
+ f0335434529d9e19359eae0fe912dd9e747667534a1c92e662f5219a55dfad8c
88
+ )
89
+ set(
90
+ f5_sha256
91
+ cfc3449c9af6863731a431ce89e32c08c5f8ece94b306fb6b695828502a76166
92
+ )
93
+ set(
94
+ f6_sha256
95
+ ec47b4783c342038e720e33b2fdfa55a9a490afb1cf37a26467733983688647e
96
+ )
97
+ set(
98
+ f7_sha256
99
+ 48a4fc8ce098305cfd4a58f40889249c523ca3c285f66ba704b5bad0e3ada53a
100
+ )
101
+ set(
102
+ f4_library_file_names
103
+ libavutil.56.dylib
104
+ libavcodec.58.dylib
105
+ libavformat.58.dylib
106
+ libavdevice.58.dylib
107
+ libavfilter.7.dylib
108
+ libswscale.5.dylib
109
+ libswresample.3.dylib
110
+ )
111
+ set(
112
+ f5_library_file_names
113
+ libavutil.57.dylib
114
+ libavcodec.59.dylib
115
+ libavformat.59.dylib
116
+ libavdevice.59.dylib
117
+ libavfilter.8.dylib
118
+ libswscale.6.dylib
119
+ libswresample.4.dylib
120
+ )
121
+ set(
122
+ f6_library_file_names
123
+ libavutil.58.dylib
124
+ libavcodec.60.dylib
125
+ libavformat.60.dylib
126
+ libavdevice.60.dylib
127
+ libavfilter.9.dylib
128
+ libswscale.7.dylib
129
+ libswresample.4.dylib
130
+ )
131
+ set(
132
+ f7_library_file_names
133
+ libavutil.59.dylib
134
+ libavcodec.61.dylib
135
+ libavformat.61.dylib
136
+ libavdevice.61.dylib
137
+ libavfilter.10.dylib
138
+ libswscale.8.dylib
139
+ libswresample.5.dylib
140
+ )
141
+ else()
142
+ message(
143
+ FATAL_ERROR
144
+ "Unsupported operating system: ${CMAKE_SYSTEM_NAME}"
145
+ )
146
+ endif()
147
+
148
+ FetchContent_Declare(
149
+ f4
150
+ URL ${platform_url}/4.4.4.tar.gz
151
+ URL_HASH
152
+ SHA256=${f4_sha256}
153
+ )
154
+ FetchContent_Declare(
155
+ f5
156
+ URL ${platform_url}/5.1.4.tar.gz
157
+ URL_HASH
158
+ SHA256=${f5_sha256}
159
+ )
160
+ FetchContent_Declare(
161
+ f6
162
+ URL ${platform_url}/6.1.1.tar.gz
163
+ URL_HASH
164
+ SHA256=${f6_sha256}
165
+ )
166
+ FetchContent_Declare(
167
+ f7
168
+ URL ${platform_url}/7.0.1.tar.gz
169
+ URL_HASH
170
+ SHA256=${f7_sha256}
171
+ )
172
+
173
+ FetchContent_MakeAvailable(f4 f5 f6 f7)
174
+
175
+ add_library(ffmpeg4 INTERFACE)
176
+ add_library(ffmpeg5 INTERFACE)
177
+ add_library(ffmpeg6 INTERFACE)
178
+ add_library(ffmpeg7 INTERFACE)
179
+
180
+ # Note: the f?_SOURCE_DIR variables were set by FetchContent_MakeAvailable
181
+ target_include_directories(ffmpeg4 INTERFACE ${f4_SOURCE_DIR}/include)
182
+ target_include_directories(ffmpeg5 INTERFACE ${f5_SOURCE_DIR}/include)
183
+ target_include_directories(ffmpeg6 INTERFACE ${f6_SOURCE_DIR}/include)
184
+ target_include_directories(ffmpeg7 INTERFACE ${f7_SOURCE_DIR}/include)
185
+
186
+ list(
187
+ TRANSFORM f4_library_file_names
188
+ PREPEND ${f4_SOURCE_DIR}/lib/
189
+ OUTPUT_VARIABLE f4_library_paths
190
+ )
191
+ list(
192
+ TRANSFORM f5_library_file_names
193
+ PREPEND ${f5_SOURCE_DIR}/lib/
194
+ OUTPUT_VARIABLE f5_library_paths
195
+ )
196
+ list(
197
+ TRANSFORM f6_library_file_names
198
+ PREPEND ${f6_SOURCE_DIR}/lib/
199
+ OUTPUT_VARIABLE f6_library_paths
200
+ )
201
+ list(
202
+ TRANSFORM f7_library_file_names
203
+ PREPEND ${f7_SOURCE_DIR}/lib/
204
+ OUTPUT_VARIABLE f7_library_paths
205
+ )
206
+
207
+ target_link_libraries(
208
+ ffmpeg4
209
+ INTERFACE
210
+ ${f4_library_paths}
211
+ )
212
+ target_link_libraries(
213
+ ffmpeg5
214
+ INTERFACE
215
+ ${f5_library_paths}
216
+ )
217
+ target_link_libraries(
218
+ ffmpeg6
219
+ INTERFACE
220
+ ${f6_library_paths}
221
+ )
222
+ target_link_libraries(
223
+ ffmpeg7
224
+ INTERFACE
225
+ ${f7_library_paths}
226
+ )
@@ -0,0 +1,381 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ import io
8
+ import json
9
+ import warnings
10
+ from types import ModuleType
11
+ from typing import List, Optional, Tuple, Union
12
+
13
+ import torch
14
+ from torch.library import get_ctx, register_fake
15
+
16
+ from torchcodec._internally_replaced_utils import ( # @manual=//pytorch/torchcodec/src:internally_replaced_utils
17
+ _get_extension_path,
18
+ _load_pybind11_module,
19
+ )
20
+
21
+ _pybind_ops: Optional[ModuleType] = None
22
+
23
+
24
+ def load_torchcodec_shared_libraries():
25
+ # Successively try to load libtorchcodec_*7.so, libtorchcodec_*6.so,
26
+ # libtorchcodec_*5.so, and libtorchcodec_*4.so. Each of these correspond to an
27
+ # ffmpeg major version. This should cover all potential ffmpeg versions
28
+ # installed on the user's machine.
29
+ #
30
+ # On fbcode, _get_extension_path() is overridden and directly points to the
31
+ # correct .so file, so this for-loop succeeds on the first iteration.
32
+ #
33
+ # Note that we use two different methods for loading shared libraries:
34
+ #
35
+ # 1. torch.ops.load_library(): For PyTorch custom ops and the C++ only
36
+ # libraries the custom ops depend on. Loading libraries through PyTorch
37
+ # registers the custom ops with PyTorch's runtime and the ops can be
38
+ # accessed through torch.ops after loading.
39
+ #
40
+ # 2. importlib: For pybind11 modules. We load them dynamically, rather
41
+ # than using a plain import statement. A plain import statement only
42
+ # works when the module name and file name match exactly. Our shared
43
+ # libraries do not meet those conditions.
44
+
45
+ exceptions = []
46
+ pybind_ops_module_name = "decoder_core_pybind_ops"
47
+ for ffmpeg_major_version in (7, 6, 5, 4):
48
+ decoder_library_name = f"libtorchcodec_decoder{ffmpeg_major_version}"
49
+ custom_ops_library_name = f"libtorchcodec_custom_ops{ffmpeg_major_version}"
50
+ pybind_ops_library_name = f"libtorchcodec_pybind_ops{ffmpeg_major_version}"
51
+ try:
52
+ torch.ops.load_library(_get_extension_path(decoder_library_name))
53
+ torch.ops.load_library(_get_extension_path(custom_ops_library_name))
54
+
55
+ pybind_ops_library_path = _get_extension_path(pybind_ops_library_name)
56
+ global _pybind_ops
57
+ _pybind_ops = _load_pybind11_module(
58
+ pybind_ops_module_name, pybind_ops_library_path
59
+ )
60
+ return
61
+ except Exception as e:
62
+ # TODO: recording and reporting exceptions this way is OK for now as it's just for debugging,
63
+ # but we should probably handle that via a proper logging mechanism.
64
+ exceptions.append((ffmpeg_major_version, e))
65
+
66
+ traceback = (
67
+ "\n[start of libtorchcodec loading traceback]\n"
68
+ + "\n".join(f"FFmpeg version {v}: {str(e)}" for v, e in exceptions)
69
+ + "\n[end of libtorchcodec loading traceback]."
70
+ )
71
+ raise RuntimeError(
72
+ f"""Could not load libtorchcodec. Likely causes:
73
+ 1. FFmpeg is not properly installed in your environment. We support
74
+ versions 4, 5, 6 and 7.
75
+ 2. The PyTorch version ({torch.__version__}) is not compatible with
76
+ this version of TorchCodec. Refer to the version compatibility
77
+ table:
78
+ https://github.com/pytorch/torchcodec?tab=readme-ov-file#installing-torchcodec.
79
+ 3. Another runtime dependency; see exceptions below.
80
+ The following exceptions were raised as we tried to load libtorchcodec:
81
+ """
82
+ f"{traceback}"
83
+ )
84
+
85
+
86
+ load_torchcodec_shared_libraries()
87
+
88
+
89
+ # Note: We use disallow_in_graph because PyTorch does constant propagation of
90
+ # factory functions.
91
+ create_from_file = torch._dynamo.disallow_in_graph(
92
+ torch.ops.torchcodec_ns.create_from_file.default
93
+ )
94
+ create_audio_encoder = torch._dynamo.disallow_in_graph(
95
+ torch.ops.torchcodec_ns.create_audio_encoder.default
96
+ )
97
+ encode_audio = torch._dynamo.disallow_in_graph(
98
+ torch.ops.torchcodec_ns.encode_audio.default
99
+ )
100
+ create_from_tensor = torch._dynamo.disallow_in_graph(
101
+ torch.ops.torchcodec_ns.create_from_tensor.default
102
+ )
103
+ _convert_to_tensor = torch._dynamo.disallow_in_graph(
104
+ torch.ops.torchcodec_ns._convert_to_tensor.default
105
+ )
106
+ add_video_stream = torch.ops.torchcodec_ns.add_video_stream.default
107
+ _add_video_stream = torch.ops.torchcodec_ns._add_video_stream.default
108
+ add_audio_stream = torch.ops.torchcodec_ns.add_audio_stream.default
109
+ seek_to_pts = torch.ops.torchcodec_ns.seek_to_pts.default
110
+ get_next_frame = torch.ops.torchcodec_ns.get_next_frame.default
111
+ get_frame_at_pts = torch.ops.torchcodec_ns.get_frame_at_pts.default
112
+ get_frame_at_index = torch.ops.torchcodec_ns.get_frame_at_index.default
113
+ get_frames_at_indices = torch.ops.torchcodec_ns.get_frames_at_indices.default
114
+ get_frames_by_pts = torch.ops.torchcodec_ns.get_frames_by_pts.default
115
+ get_frames_in_range = torch.ops.torchcodec_ns.get_frames_in_range.default
116
+ get_frames_by_pts_in_range = torch.ops.torchcodec_ns.get_frames_by_pts_in_range.default
117
+ get_frames_by_pts_in_range_audio = (
118
+ torch.ops.torchcodec_ns.get_frames_by_pts_in_range_audio.default
119
+ )
120
+ get_json_metadata = torch.ops.torchcodec_ns.get_json_metadata.default
121
+ _test_frame_pts_equality = torch.ops.torchcodec_ns._test_frame_pts_equality.default
122
+ _get_container_json_metadata = (
123
+ torch.ops.torchcodec_ns.get_container_json_metadata.default
124
+ )
125
+ _get_key_frame_indices = torch.ops.torchcodec_ns._get_key_frame_indices.default
126
+ scan_all_streams_to_update_metadata = (
127
+ torch.ops.torchcodec_ns.scan_all_streams_to_update_metadata.default
128
+ )
129
+ _get_stream_json_metadata = torch.ops.torchcodec_ns.get_stream_json_metadata.default
130
+ _get_json_ffmpeg_library_versions = (
131
+ torch.ops.torchcodec_ns._get_json_ffmpeg_library_versions.default
132
+ )
133
+
134
+
135
+ # =============================
136
+ # Functions not related to custom ops, but similar implementation to c++ ops
137
+ # =============================
138
+ def create_from_bytes(
139
+ video_bytes: bytes, seek_mode: Optional[str] = None
140
+ ) -> torch.Tensor:
141
+ with warnings.catch_warnings():
142
+ # Ignore warning stating that the underlying video_bytes buffer is
143
+ # non-writable.
144
+ warnings.filterwarnings("ignore", category=UserWarning)
145
+ buffer = torch.frombuffer(video_bytes, dtype=torch.uint8)
146
+ return create_from_tensor(buffer, seek_mode)
147
+
148
+
149
+ def create_from_file_like(
150
+ file_like: Union[io.RawIOBase, io.BufferedReader], seek_mode: Optional[str] = None
151
+ ) -> torch.Tensor:
152
+ assert _pybind_ops is not None
153
+ return _convert_to_tensor(_pybind_ops.create_from_file_like(file_like, seek_mode))
154
+
155
+
156
+ # ==============================
157
+ # Abstract impl for the operators. Needed by torch.compile.
158
+ # ==============================
159
+ @register_fake("torchcodec_ns::create_from_file")
160
+ def create_from_file_abstract(filename: str, seek_mode: Optional[str]) -> torch.Tensor:
161
+ return torch.empty([], dtype=torch.long)
162
+
163
+
164
+ @register_fake("torchcodec_ns::create_audio_encoder")
165
+ def create_audio_encoder_abstract(
166
+ wf: torch.Tensor, sample_rate: int, filename: str, bit_rate: Optional[int] = None
167
+ ) -> torch.Tensor:
168
+ return torch.empty([], dtype=torch.long)
169
+
170
+
171
+ @register_fake("torchcodec_ns::encode_audio")
172
+ def encode_audio_abstract(encoder: torch.Tensor) -> torch.Tensor:
173
+ return torch.empty([], dtype=torch.long)
174
+
175
+
176
+ @register_fake("torchcodec_ns::create_from_tensor")
177
+ def create_from_tensor_abstract(
178
+ video_tensor: torch.Tensor, seek_mode: Optional[str]
179
+ ) -> torch.Tensor:
180
+ return torch.empty([], dtype=torch.long)
181
+
182
+
183
+ @register_fake("torchcodec_ns::_convert_to_tensor")
184
+ def _convert_to_tensor_abstract(decoder_ptr: int) -> torch.Tensor:
185
+ return torch.empty([], dtype=torch.long)
186
+
187
+
188
+ @register_fake("torchcodec_ns::_add_video_stream")
189
+ def _add_video_stream_abstract(
190
+ decoder: torch.Tensor,
191
+ *,
192
+ width: Optional[int] = None,
193
+ height: Optional[int] = None,
194
+ num_threads: Optional[int] = None,
195
+ dimension_order: Optional[str] = None,
196
+ stream_index: Optional[int] = None,
197
+ device: Optional[str] = None,
198
+ color_conversion_library: Optional[str] = None,
199
+ ) -> None:
200
+ return
201
+
202
+
203
+ @register_fake("torchcodec_ns::add_video_stream")
204
+ def add_video_stream_abstract(
205
+ decoder: torch.Tensor,
206
+ *,
207
+ width: Optional[int] = None,
208
+ height: Optional[int] = None,
209
+ num_threads: Optional[int] = None,
210
+ dimension_order: Optional[str] = None,
211
+ stream_index: Optional[int] = None,
212
+ device: Optional[str] = None,
213
+ ) -> None:
214
+ return
215
+
216
+
217
+ @register_fake("torchcodec_ns::add_audio_stream")
218
+ def add_audio_stream_abstract(
219
+ decoder: torch.Tensor,
220
+ *,
221
+ stream_index: Optional[int] = None,
222
+ ) -> None:
223
+ return
224
+
225
+
226
+ @register_fake("torchcodec_ns::seek_to_pts")
227
+ def seek_abstract(decoder: torch.Tensor, seconds: float) -> None:
228
+ return
229
+
230
+
231
+ @register_fake("torchcodec_ns::get_next_frame")
232
+ def get_next_frame_abstract(
233
+ decoder: torch.Tensor,
234
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
235
+ # Images are 3 dimensions: height, width, channels.
236
+ # The exact permutation depends on the constructor options passed in.
237
+ image_size = [get_ctx().new_dynamic_size() for _ in range(3)]
238
+ return (
239
+ torch.empty(image_size),
240
+ torch.empty([], dtype=torch.float),
241
+ torch.empty([], dtype=torch.float),
242
+ )
243
+
244
+
245
+ @register_fake("torchcodec_ns::get_frame_at_pts")
246
+ def get_frame_at_pts_abstract(
247
+ decoder: torch.Tensor, seconds: float
248
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
249
+ image_size = [get_ctx().new_dynamic_size() for _ in range(3)]
250
+ return (
251
+ torch.empty(image_size),
252
+ torch.empty([], dtype=torch.float),
253
+ torch.empty([], dtype=torch.float),
254
+ )
255
+
256
+
257
+ @register_fake("torchcodec_ns::get_frames_by_pts")
258
+ def get_frames_by_pts_abstract(
259
+ decoder: torch.Tensor,
260
+ *,
261
+ timestamps: List[float],
262
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
263
+ image_size = [get_ctx().new_dynamic_size() for _ in range(4)]
264
+ return (
265
+ torch.empty(image_size),
266
+ torch.empty([], dtype=torch.float),
267
+ torch.empty([], dtype=torch.float),
268
+ )
269
+
270
+
271
+ @register_fake("torchcodec_ns::get_frame_at_index")
272
+ def get_frame_at_index_abstract(
273
+ decoder: torch.Tensor, *, frame_index: int
274
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
275
+ image_size = [get_ctx().new_dynamic_size() for _ in range(3)]
276
+ return (
277
+ torch.empty(image_size),
278
+ torch.empty([], dtype=torch.float),
279
+ torch.empty([], dtype=torch.float),
280
+ )
281
+
282
+
283
+ @register_fake("torchcodec_ns::get_frames_at_indices")
284
+ def get_frames_at_indices_abstract(
285
+ decoder: torch.Tensor,
286
+ *,
287
+ frame_indices: List[int],
288
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
289
+ image_size = [get_ctx().new_dynamic_size() for _ in range(4)]
290
+ return (
291
+ torch.empty(image_size),
292
+ torch.empty([], dtype=torch.float),
293
+ torch.empty([], dtype=torch.float),
294
+ )
295
+
296
+
297
+ @register_fake("torchcodec_ns::get_frames_in_range")
298
+ def get_frames_in_range_abstract(
299
+ decoder: torch.Tensor,
300
+ *,
301
+ start: int,
302
+ stop: int,
303
+ step: Optional[int] = None,
304
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
305
+ image_size = [get_ctx().new_dynamic_size() for _ in range(4)]
306
+ return (
307
+ torch.empty(image_size),
308
+ torch.empty([], dtype=torch.float),
309
+ torch.empty([], dtype=torch.float),
310
+ )
311
+
312
+
313
+ @register_fake("torchcodec_ns::get_frames_by_pts_in_range")
314
+ def get_frames_by_pts_in_range_abstract(
315
+ decoder: torch.Tensor,
316
+ *,
317
+ start_seconds: float,
318
+ stop_seconds: float,
319
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
320
+ image_size = [get_ctx().new_dynamic_size() for _ in range(4)]
321
+ return (
322
+ torch.empty(image_size),
323
+ torch.empty([], dtype=torch.float),
324
+ torch.empty([], dtype=torch.float),
325
+ )
326
+
327
+
328
+ @register_fake("torchcodec_ns::get_frames_by_pts_in_range_audio")
329
+ def get_frames_by_pts_in_range_audio_abstract(
330
+ decoder: torch.Tensor,
331
+ *,
332
+ start_seconds: float,
333
+ stop_seconds: Optional[float] = None,
334
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
335
+ image_size = [get_ctx().new_dynamic_size() for _ in range(4)]
336
+ return (torch.empty(image_size), torch.empty([], dtype=torch.float))
337
+
338
+
339
+ @register_fake("torchcodec_ns::_get_key_frame_indices")
340
+ def get_key_frame_indices_abstract(decoder: torch.Tensor) -> torch.Tensor:
341
+ return torch.empty([], dtype=torch.int)
342
+
343
+
344
+ @register_fake("torchcodec_ns::get_json_metadata")
345
+ def get_json_metadata_abstract(decoder: torch.Tensor) -> str:
346
+ return ""
347
+
348
+
349
+ @register_fake("torchcodec_ns::get_container_json_metadata")
350
+ def get_container_json_metadata_abstract(decoder: torch.Tensor) -> str:
351
+ return ""
352
+
353
+
354
+ @register_fake("torchcodec_ns::get_stream_json_metadata")
355
+ def get_stream_json_metadata_abstract(decoder: torch.Tensor, stream_idx: int) -> str:
356
+ return ""
357
+
358
+
359
+ @register_fake("torchcodec_ns::_test_frame_pts_equality")
360
+ def _test_frame_pts_equality_abstract(
361
+ decoder: torch.Tensor,
362
+ *,
363
+ frame_index: int,
364
+ pts_seconds_to_test: float,
365
+ ) -> bool:
366
+ return False
367
+
368
+
369
+ @register_fake("torchcodec_ns::_get_json_ffmpeg_library_versions")
370
+ def _get_json_ffmpeg_library_versions_abstract() -> str:
371
+ return ""
372
+
373
+
374
+ @register_fake("torchcodec_ns::scan_all_streams_to_update_metadata")
375
+ def scan_all_streams_to_update_metadata_abstract(decoder: torch.Tensor) -> None:
376
+ return
377
+
378
+
379
+ def get_ffmpeg_library_versions():
380
+ versions_json = _get_json_ffmpeg_library_versions()
381
+ return json.loads(versions_json)
@@ -0,0 +1,45 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ // All rights reserved.
3
+ //
4
+ // This source code is licensed under the BSD-style license found in the
5
+ // LICENSE file in the root directory of this source tree.
6
+
7
+ #include <pybind11/pybind11.h>
8
+ #include <pybind11/stl.h>
9
+ #include <cstdint>
10
+ #include <string>
11
+
12
+ #include "src/torchcodec/_core/AVIOFileLikeContext.h"
13
+ #include "src/torchcodec/_core/SingleStreamDecoder.h"
14
+
15
+ namespace py = pybind11;
16
+
17
+ namespace facebook::torchcodec {
18
+
19
+ // In principle, this should be able to return a tensor. But when we try that,
20
+ // we run into the bug reported here:
21
+ //
22
+ // https://github.com/pytorch/pytorch/issues/136664
23
+ //
24
+ // So we instead launder the pointer through an int, and then use a conversion
25
+ // function on the custom ops side to launder that int into a tensor.
26
+ int64_t create_from_file_like(
27
+ py::object file_like,
28
+ std::optional<std::string_view> seek_mode) {
29
+ SingleStreamDecoder::SeekMode realSeek = SingleStreamDecoder::SeekMode::exact;
30
+ if (seek_mode.has_value()) {
31
+ realSeek = seekModeFromString(seek_mode.value());
32
+ }
33
+
34
+ auto avioContextHolder = std::make_unique<AVIOFileLikeContext>(file_like);
35
+
36
+ SingleStreamDecoder* decoder =
37
+ new SingleStreamDecoder(std::move(avioContextHolder), realSeek);
38
+ return reinterpret_cast<int64_t>(decoder);
39
+ }
40
+
41
+ PYBIND11_MODULE(decoder_core_pybind_ops, m) {
42
+ m.def("create_from_file_like", &create_from_file_like);
43
+ }
44
+
45
+ } // namespace facebook::torchcodec