torchcodec 0.3.0__cp312-cp312-macosx_11_0_arm64.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.
- torchcodec/.dylibs/libc++.1.0.dylib +0 -0
- torchcodec/.dylibs/libpython3.12.dylib +0 -0
- torchcodec/__init__.py +16 -0
- torchcodec/_core/AVIOBytesContext.cpp +70 -0
- torchcodec/_core/AVIOBytesContext.h +32 -0
- torchcodec/_core/AVIOContextHolder.cpp +50 -0
- torchcodec/_core/AVIOContextHolder.h +65 -0
- torchcodec/_core/AVIOFileLikeContext.cpp +80 -0
- torchcodec/_core/AVIOFileLikeContext.h +54 -0
- torchcodec/_core/CMakeLists.txt +237 -0
- torchcodec/_core/CudaDeviceInterface.cpp +289 -0
- torchcodec/_core/CudaDeviceInterface.h +34 -0
- torchcodec/_core/DeviceInterface.cpp +88 -0
- torchcodec/_core/DeviceInterface.h +66 -0
- torchcodec/_core/Encoder.cpp +319 -0
- torchcodec/_core/Encoder.h +39 -0
- torchcodec/_core/FFMPEGCommon.cpp +264 -0
- torchcodec/_core/FFMPEGCommon.h +180 -0
- torchcodec/_core/Frame.h +47 -0
- torchcodec/_core/Metadata.h +70 -0
- torchcodec/_core/SingleStreamDecoder.cpp +1947 -0
- torchcodec/_core/SingleStreamDecoder.h +462 -0
- torchcodec/_core/StreamOptions.h +49 -0
- torchcodec/_core/__init__.py +39 -0
- torchcodec/_core/_metadata.py +277 -0
- torchcodec/_core/custom_ops.cpp +681 -0
- torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +226 -0
- torchcodec/_core/ops.py +381 -0
- torchcodec/_core/pybind_ops.cpp +45 -0
- torchcodec/_frame.py +145 -0
- torchcodec/_internally_replaced_utils.py +53 -0
- torchcodec/_samplers/__init__.py +7 -0
- torchcodec/_samplers/video_clip_sampler.py +430 -0
- torchcodec/decoders/__init__.py +11 -0
- torchcodec/decoders/_audio_decoder.py +168 -0
- torchcodec/decoders/_decoder_utils.py +52 -0
- torchcodec/decoders/_video_decoder.py +399 -0
- torchcodec/libtorchcodec_custom_ops4.dylib +0 -0
- torchcodec/libtorchcodec_custom_ops5.dylib +0 -0
- torchcodec/libtorchcodec_custom_ops6.dylib +0 -0
- torchcodec/libtorchcodec_custom_ops7.dylib +0 -0
- torchcodec/libtorchcodec_decoder4.dylib +0 -0
- torchcodec/libtorchcodec_decoder5.dylib +0 -0
- torchcodec/libtorchcodec_decoder6.dylib +0 -0
- torchcodec/libtorchcodec_decoder7.dylib +0 -0
- torchcodec/libtorchcodec_pybind_ops4.so +0 -0
- torchcodec/libtorchcodec_pybind_ops5.so +0 -0
- torchcodec/libtorchcodec_pybind_ops6.so +0 -0
- torchcodec/libtorchcodec_pybind_ops7.so +0 -0
- torchcodec/samplers/__init__.py +2 -0
- torchcodec/samplers/_common.py +84 -0
- torchcodec/samplers/_index_based.py +285 -0
- torchcodec/samplers/_time_based.py +348 -0
- torchcodec/version.py +2 -0
- torchcodec-0.3.0.dist-info/LICENSE +28 -0
- torchcodec-0.3.0.dist-info/METADATA +280 -0
- torchcodec-0.3.0.dist-info/RECORD +59 -0
- torchcodec-0.3.0.dist-info/WHEEL +5 -0
- 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
|
+
)
|
torchcodec/_core/ops.py
ADDED
|
@@ -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
|