From b1c077f7d6fa0bd3768ff787c389a5e7b83991be Mon Sep 17 00:00:00 2001 From: Dmitry Rogozhkin Date: Fri, 3 Oct 2025 11:15:54 -0700 Subject: [PATCH 01/10] Expose TorchCodecConfig.cmake This commit exposes torchcodec core library to be used by third party modules on the C++ level. The primary purpose is to allow non-CUDA device interfaces out-of-tree implementations. There are the following major changes: * Exposed TorchCodecConfig.cmake which defines torchcodec targets to be linked with * Provided Python level APIs to faciliate out-of-tree device interfaces work with torchcodec: * `torchcodec.cmake_prefix_path` - path which points to `TorchCodecConfig.cmake` configuration * `torchcodec.variant` - variant of the torchcodec library which was loaded, i.e. N in libtorchcodec_core{N}.so (currently ffmpeg_major_version) * `torchcodec.core_library_path` - full path of the loaded torchcodec core library * `src/torchcodec/_core/` dropped from include paths to allow using of the core library out-of-tree `TorchCodecConfig.cmake` has 2 working modes: * By default config works by checking available version of FFmpeg libraries via `pkg-config` and configures corresponding (single) version of torchcodec * Altenatively, if `TORCHCODEC_FFMPEG{N}_INSTALL_PREFIX` is set (`N=4,5,6,7` - version of FFmpeg), then config defines torchcodec target corresponding to the specified FFmpeg version. Note that multiple prefixes can be specified at the same time allowing to build against few torchcodec versions at once. Config will define `TORCHCODEC_VARIANTS` variable with value corresponding to FFmpeg major versions of available torchcodec core libraries. Further, config will also define `torchcodec::ffmpeg${N}` and `torchcodec::core${N}` targets where `N` takes values from `TORCHCODEC_VARIANTS`. Signed-off-by: Dmitry Rogozhkin --- src/torchcodec/__init__.py | 5 + src/torchcodec/_core/__init__.py | 2 + src/torchcodec/_core/ops.py | 10 +- .../cmake/TorchCodec/TorchCodecConfig.cmake | 185 ++++++++++++++++++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake diff --git a/src/torchcodec/__init__.py b/src/torchcodec/__init__.py index 2b6c9fb3e..b3ee534ab 100644 --- a/src/torchcodec/__init__.py +++ b/src/torchcodec/__init__.py @@ -4,8 +4,11 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. +import os.path as _osp + # Note: usort wants to put Frame and FrameBatch after decoders and samplers, # but that results in circular import. +from ._core import core_library_path, variant from ._frame import AudioSamples, Frame, FrameBatch # usort:skip # noqa from . import decoders, samplers # noqa @@ -14,3 +17,5 @@ from .version import __version__ # noqa: F401 except Exception: pass + +cmake_prefix_path = _osp.join(_osp.dirname(__file__), "share", "cmake") diff --git a/src/torchcodec/_core/__init__.py b/src/torchcodec/_core/__init__.py index 55ff697b3..667e61121 100644 --- a/src/torchcodec/_core/__init__.py +++ b/src/torchcodec/_core/__init__.py @@ -19,6 +19,7 @@ _test_frame_pts_equality, add_audio_stream, add_video_stream, + core_library_path, create_from_bytes, create_from_file, create_from_file_like, @@ -41,4 +42,5 @@ get_next_frame, scan_all_streams_to_update_metadata, seek_to_pts, + variant, ) diff --git a/src/torchcodec/_core/ops.py b/src/torchcodec/_core/ops.py index 32995c964..fa3d4e843 100644 --- a/src/torchcodec/_core/ops.py +++ b/src/torchcodec/_core/ops.py @@ -6,6 +6,7 @@ import io import json +import os import warnings from types import ModuleType from typing import List, Optional, Tuple, Union @@ -21,6 +22,8 @@ _pybind_ops: Optional[ModuleType] = None +variant = None +core_library_path = None def load_torchcodec_shared_libraries(): # Successively try to load the shared libraries for each version of FFmpeg @@ -47,7 +50,8 @@ def load_torchcodec_shared_libraries(): custom_ops_library_name = f"libtorchcodec_custom_ops{ffmpeg_major_version}" pybind_ops_library_name = f"libtorchcodec_pybind_ops{ffmpeg_major_version}" try: - torch.ops.load_library(_get_extension_path(decoder_library_name)) + decoder_library_path = _get_extension_path(decoder_library_name) + torch.ops.load_library(decoder_library_path) torch.ops.load_library(_get_extension_path(custom_ops_library_name)) pybind_ops_library_path = _get_extension_path(pybind_ops_library_name) @@ -55,6 +59,10 @@ def load_torchcodec_shared_libraries(): _pybind_ops = _load_pybind11_module( pybind_ops_module_name, pybind_ops_library_path ) + global variant + global core_library_path + variant = ffmpeg_major_version + core_library_path = decoder_library_path return except Exception as e: # TODO: recording and reporting exceptions this way is OK for now as it's just for debugging, diff --git a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake new file mode 100644 index 000000000..33471e002 --- /dev/null +++ b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake @@ -0,0 +1,185 @@ +# FindTorchCodec +# -------------- +# +# Finds the TorchCodec library +# +# This will define the following variables: +# +# TORCHCODEC_FOUND -- True if the system has the TorchCodec library +# TORCHCODEC_VARIANTS -- List of TorchCodec variants +# +# and the following imported targets: +# +# torchcodec::ffmpeg${N} +# torchcodec::core${N} +# +# where N is a TorchCodec variant from TORCHCODEC_VARIANTS list. + +include(FindPackageHandleStandardArgs) + +# Assume we are in /share/cmake/TorchCodec/TorchCodecConfig.cmake +get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(TORCHCODEC_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +# Include directories. +set(TORCHCODEC_INCLUDE_DIRS ${TORCHCODEC_INSTALL_PREFIX}/_core) +set(TORCHCODEC_VARIANTS "") + +function(add_ffmpeg_target ffmpeg_major_version libs) + set(target "torchcodec::ffmpeg${ffmpeg_major_version}") + set(prefix "TORCHCODEC_FFMPEG${ffmpeg_major_version}_INSTALL_PREFIX") + if (NOT DEFINED ENV{${prefix}}) + message("Skipping ${target} as ${prefix} is not defined") + return() + endif() + + set(prefix "$ENV{${prefix}}") + set(incdir "${prefix}/include") + if (UNIX AND NOT APPLE) + set(libdir "${prefix}/lib") + else() + message("Skipping ${target} on non-Linux platform") + return() + endif() + + set(lib_paths "") + foreach(lib IN LISTS libs) + find_library(_LIB_PATH "${lib}" PATHS "${libdir}" NO_DEFAULT_PATH) + if (NOT _LIB_PATH) + message("Skipping ${target} as ${lib} is missing") + return() + else() + list(APPEND lib_paths "${_LIB_PATH}") + endif() + # Removing _LIB_PATH from cache otherwise it won't be updated + # on the next call to find_library(). + unset(_LIB_PATH CACHE) + endforeach() + + message("Adding ${target} target") + add_library(${target} SHARED IMPORTED) + set_target_properties(${target} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${incdir} + IMPORTED_LOCATION ${lib_paths} + ) +endfunction() + +function(add_torchcodec_target ffmpeg_major_version) + set(target torchcodec::core${ffmpeg_major_version}) + + if (NOT TARGET torchcodec::ffmpeg${ffmpeg_major_version}) + message("Skipping ${target} as torchcodec::ffmpeg${ffmpeg_major_version} is not defined") + return() + endif() + + find_library(_LIB_PATH torchcodec_core${ffmpeg_major_version} + PATHS "${TORCHCODEC_INSTALL_PREFIX}" NO_CACHE NO_DEFAULT_PATH) + if (NOT _LIB_PATH) + message("Skipping ${target} as torchcodec_core${ffmpeg_major_version} is missing") + return() + endif() + + message("Adding ${target} target") + add_library(${target} SHARED IMPORTED) + add_dependencies(${target} torchcodec::ffmpeg${ffmpeg_major_version}) + set_target_properties(${target} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${TORCHCODEC_INCLUDE_DIRS} + IMPORTED_LOCATION ${_LIB_PATH} + ) + # Removing _LIB_PATH from cache otherwise it won't be updated + # on the next call to find_library(). + unset(_LIB_PATH CACHE) + + list(APPEND TORCHCODEC_VARIANTS "${ffmpeg_major_version}") + set(TORCHCODEC_VARIANTS "${TORCHCODEC_VARIANTS}" PARENT_SCOPE) +endfunction() + +if (DEFINED ENV{TORCHCODEC_FFMPEG4_INSTALL_PREFIX} OR + DEFINED ENV{TORCHCODEC_FFMPEG5_INSTALL_PREFIX} OR + DEFINED ENV{TORCHCODEC_FFMPEG6_INSTALL_PREFIX} OR + DEFINED ENV{TORCHCODEC_FFMPEG7_INSTALL_PREFIX}) + if (UNIX AND NOT APPLE) + set(f4_library_file_names + libavutil.so.56 + libavcodec.so.58 + libavformat.so.58 + libavdevice.so.58 + libavfilter.so.7 + libswscale.so.5 + libswresample.so.3 + ) + set(f5_library_file_names + libavutil.so.57 + libavcodec.so.59 + libavformat.so.59 + libavdevice.so.59 + libavfilter.so.8 + libswscale.so.6 + libswresample.so.4 + ) + set(f6_library_file_names + libavutil.so.58 + libavcodec.so.60 + libavformat.so.60 + libavdevice.so.60 + libavfilter.so.9 + libswscale.so.7 + libswresample.so.4 + ) + set(f7_library_file_names + libavutil.so.59 + libavcodec.so.61 + libavformat.so.61 + libavdevice.so.61 + libavfilter.so.10 + libswscale.so.8 + libswresample.so.5 + ) + endif() + + add_ffmpeg_target(4 "${f4_library_file_names}") + add_ffmpeg_target(5 "${f5_library_file_names}") + add_ffmpeg_target(6 "${f6_library_file_names}") + add_ffmpeg_target(7 "${f7_library_file_names}") + + add_torchcodec_target(4) + add_torchcodec_target(5) + add_torchcodec_target(6) + add_torchcodec_target(7) +else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(TORCHCODEC_LIBAV IMPORTED_TARGET + libavdevice + libavfilter + libavformat + libavcodec + libavutil + libswresample + libswscale + ) + + if (TARGET PkgConfig::TORCHCODEC_LIBAV) + # Split libavcodec's version string by '.' and convert it to a list + string(REPLACE "." ";" libavcodec_version_list ${TORCHCODEC_LIBAV_libavcodec_VERSION}) + # Get the first element of the list, which is the major version + list(GET libavcodec_version_list 0 libavcodec_major_version) + + if (${libavcodec_major_version} STREQUAL "58") + set(ffmpeg_major_version "4") + elseif (${libavcodec_major_version} STREQUAL "59") + set(ffmpeg_major_version "5") + elseif (${libavcodec_major_version} STREQUAL "60") + set(ffmpeg_major_version "6") + elseif (${libavcodec_major_version} STREQUAL "61") + set(ffmpeg_major_version "7") + endif() + + if (libavcodec_major_version) + message("Adding torchcodec::ffmpeg${ffmpeg_major_version} target") + add_library(torchcodec::ffmpeg${ffmpeg_major_version} ALIAS PkgConfig::TORCHCODEC_LIBAV) + add_torchcodec_target(${ffmpeg_major_version}) + endif() + endif() +endif() + +find_package_handle_standard_args(TorchCodec DEFAULT_MSG TORCHCODEC_VARIANTS) From 1e19551debc60d3f0d903fb15e6c58256353dd5d Mon Sep 17 00:00:00 2001 From: Dmitry Rogozhkin Date: Wed, 29 Oct 2025 12:16:22 -0700 Subject: [PATCH 02/10] Share ffmpeg version setups and other fixes Signed-off-by: Dmitry Rogozhkin tmp Signed-off-by: Dmitry Rogozhkin --- src/torchcodec/__init__.py | 11 +- src/torchcodec/_core/CMakeLists.txt | 48 +-- src/torchcodec/_core/__init__.py | 2 +- ...fetch_and_expose_non_gpl_ffmpeg_libs.cmake | 226 +------------- src/torchcodec/_core/ops.py | 16 +- .../cmake/TorchCodec/TorchCodecConfig.cmake | 142 +-------- .../cmake/TorchCodec/ffmpeg_versions.cmake | 277 ++++++++++++++++++ 7 files changed, 325 insertions(+), 397 deletions(-) create mode 100644 src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake diff --git a/src/torchcodec/__init__.py b/src/torchcodec/__init__.py index b3ee534ab..fec1f8c79 100644 --- a/src/torchcodec/__init__.py +++ b/src/torchcodec/__init__.py @@ -4,11 +4,11 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -import os.path as _osp +from pathlib import Path # Note: usort wants to put Frame and FrameBatch after decoders and samplers, # but that results in circular import. -from ._core import core_library_path, variant +from ._core import core_library_path, ffmpeg_major_version from ._frame import AudioSamples, Frame, FrameBatch # usort:skip # noqa from . import decoders, samplers # noqa @@ -18,4 +18,9 @@ except Exception: pass -cmake_prefix_path = _osp.join(_osp.dirname(__file__), "share", "cmake") +# `torchcodec.cmake_prefix_path` is a Python-based way to programmatically +# obtain the correct CMAKE_PREFIX_PATH value for the TorchCodec installation. +# It can be used in a build system of a C++ application to ensure that CMake +# can successfully find TorchCodec C++ libraries. This variable is exposed +# as TorchCodec API. +cmake_prefix_path = Path(__file__).parent / "share" / "cmake" diff --git a/src/torchcodec/_core/CMakeLists.txt b/src/torchcodec/_core/CMakeLists.txt index 6b4ccb5d4..cecbf863a 100644 --- a/src/torchcodec/_core/CMakeLists.txt +++ b/src/torchcodec/_core/CMakeLists.txt @@ -245,16 +245,16 @@ if(DEFINED ENV{BUILD_AGAINST_ALL_FFMPEG_FROM_S3}) you still need a different FFmpeg to be installed for run time!" ) - # This will expose the ffmpeg4, ffmpeg5, ffmpeg6, ffmpeg7, and ffmpeg8 targets + # This will expose the torchcodec::ffmpeg{N} (N=4,5,6,7,8) targets include( ${CMAKE_CURRENT_SOURCE_DIR}/fetch_and_expose_non_gpl_ffmpeg_libs.cmake ) - make_torchcodec_libraries(8 ffmpeg8) - make_torchcodec_libraries(7 ffmpeg7) - make_torchcodec_libraries(6 ffmpeg6) - make_torchcodec_libraries(4 ffmpeg4) - make_torchcodec_libraries(5 ffmpeg5) + make_torchcodec_libraries(8 torchcodec::ffmpeg8) + make_torchcodec_libraries(7 torchcodec::ffmpeg7) + make_torchcodec_libraries(6 torchcodec::ffmpeg6) + make_torchcodec_libraries(4 torchcodec::ffmpeg4) + make_torchcodec_libraries(5 torchcodec::ffmpeg5) else() message( STATUS @@ -263,40 +263,12 @@ else() installed FFmpeg from conda, make sure pkg-config is installed from conda as well." ) - find_package(PkgConfig REQUIRED) - pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET - libavdevice - libavfilter - libavformat - libavcodec - libavutil - libswresample - libswscale - ) - # Split libavcodec's version string by '.' and convert it to a list - string(REPLACE "." ";" libavcodec_version_list ${LIBAV_libavcodec_VERSION}) - # Get the first element of the list, which is the major version - list(GET libavcodec_version_list 0 libavcodec_major_version) - - if (${libavcodec_major_version} STREQUAL "58") - set(ffmpeg_major_version "4") - elseif (${libavcodec_major_version} STREQUAL "59") - set(ffmpeg_major_version "5") - elseif (${libavcodec_major_version} STREQUAL "60") - set(ffmpeg_major_version "6") - elseif (${libavcodec_major_version} STREQUAL "61") - set(ffmpeg_major_version "7") - elseif (${libavcodec_major_version} STREQUAL "62") - set(ffmpeg_major_version "8") - else() - message( - FATAL_ERROR - "Unsupported libavcodec version: ${libavcodec_major_version}" - ) - endif() + # This will expose `add_ffmpeg_target_with_pkg_config` + include("${CMAKE_CURRENT_SOURCE_DIR}/../share/cmake/TorchCodec/ffmpeg_versions.cmake") - make_torchcodec_libraries(${ffmpeg_major_version} PkgConfig::LIBAV) + add_ffmpeg_target_with_pkg_config(ffmpeg_major_version) + make_torchcodec_libraries(${ffmpeg_major_version} torchcodec::ffmpeg${ffmpeg_major_version}) # Expose these values updwards so that the test compilation does not need # to re-figure it out. FIXME: it's not great that we just copy-paste the diff --git a/src/torchcodec/_core/__init__.py b/src/torchcodec/_core/__init__.py index 667e61121..add9efa90 100644 --- a/src/torchcodec/_core/__init__.py +++ b/src/torchcodec/_core/__init__.py @@ -30,6 +30,7 @@ encode_video_to_file, encode_video_to_file_like, encode_video_to_tensor, + ffmpeg_major_version, get_ffmpeg_library_versions, get_frame_at_index, get_frame_at_pts, @@ -42,5 +43,4 @@ get_next_frame, scan_all_streams_to_update_metadata, seek_to_pts, - variant, ) diff --git a/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake b/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake index 07abd2e87..17a84392c 100644 --- a/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +++ b/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake @@ -21,8 +21,6 @@ set( ) if (LINUX) - set(lib_dir "lib") - set( platform_url ${base_url}/linux_x86_64 @@ -48,58 +46,7 @@ if (LINUX) f8_sha256 c55b3c1a4b5e4d5fdd7c632bea3ab6f45b4e37cc8e0999dda3f84a8ed8defad8 ) - set( - f4_library_file_names - libavutil.so.56 - libavcodec.so.58 - libavformat.so.58 - libavdevice.so.58 - libavfilter.so.7 - libswscale.so.5 - libswresample.so.3 - ) - set( - f5_library_file_names - libavutil.so.57 - libavcodec.so.59 - libavformat.so.59 - libavdevice.so.59 - libavfilter.so.8 - libswscale.so.6 - libswresample.so.4 - ) - set( - f6_library_file_names - libavutil.so.58 - libavcodec.so.60 - libavformat.so.60 - libavdevice.so.60 - libavfilter.so.9 - libswscale.so.7 - libswresample.so.4 - ) - set( - f7_library_file_names - libavutil.so.59 - libavcodec.so.61 - libavformat.so.61 - libavdevice.so.61 - libavfilter.so.10 - libswscale.so.8 - libswresample.so.5 - ) - set( - f8_library_file_names - libavutil.so.60 - libavcodec.so.62 - libavformat.so.62 - libavdevice.so.62 - libavfilter.so.11 - libswscale.so.9 - libswresample.so.6 - ) elseif (APPLE) - set(lib_dir "lib") set( platform_url ${base_url}/macos_arm64 @@ -124,60 +71,7 @@ elseif (APPLE) f8_sha256 beb936b76f25d2621228a12cdb67c9ae3d1eff7aa713ef8d1167ebf0c25bd5ec ) - - set( - f4_library_file_names - libavutil.56.dylib - libavcodec.58.dylib - libavformat.58.dylib - libavdevice.58.dylib - libavfilter.7.dylib - libswscale.5.dylib - libswresample.3.dylib - ) - set( - f5_library_file_names - libavutil.57.dylib - libavcodec.59.dylib - libavformat.59.dylib - libavdevice.59.dylib - libavfilter.8.dylib - libswscale.6.dylib - libswresample.4.dylib - ) - set( - f6_library_file_names - libavutil.58.dylib - libavcodec.60.dylib - libavformat.60.dylib - libavdevice.60.dylib - libavfilter.9.dylib - libswscale.7.dylib - libswresample.4.dylib - ) - set( - f7_library_file_names - libavutil.59.dylib - libavcodec.61.dylib - libavformat.61.dylib - libavdevice.61.dylib - libavfilter.10.dylib - libswscale.8.dylib - libswresample.5.dylib - ) - set( - f8_library_file_names - libavutil.60.dylib - libavcodec.62.dylib - libavformat.62.dylib - libavdevice.62.dylib - libavfilter.11.dylib - libswscale.9.dylib - libswresample.6.dylib - ) - elseif (WIN32) - set(lib_dir "bin") set( platform_url ${base_url}/windows_x86_64 @@ -202,57 +96,6 @@ elseif (WIN32) f8_sha256 bac845ac79876b104959cb0e7b9dec772a261116344dd17d2f97e7ddfac4a73f ) - - set( - f4_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f5_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f6_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f7_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f8_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) else() message( FATAL_ERROR @@ -293,68 +136,11 @@ FetchContent_Declare( FetchContent_MakeAvailable(f4 f5 f6 f7 f8) -add_library(ffmpeg4 INTERFACE) -add_library(ffmpeg5 INTERFACE) -add_library(ffmpeg6 INTERFACE) -add_library(ffmpeg7 INTERFACE) -add_library(ffmpeg8 INTERFACE) +include("${CMAKE_CURRENT_SOURCE_DIR}/../share/cmake/TorchCodec/ffmpeg_versions.cmake") # Note: the f?_SOURCE_DIR variables were set by FetchContent_MakeAvailable -target_include_directories(ffmpeg4 INTERFACE ${f4_SOURCE_DIR}/include) -target_include_directories(ffmpeg5 INTERFACE ${f5_SOURCE_DIR}/include) -target_include_directories(ffmpeg6 INTERFACE ${f6_SOURCE_DIR}/include) -target_include_directories(ffmpeg7 INTERFACE ${f7_SOURCE_DIR}/include) -target_include_directories(ffmpeg8 INTERFACE ${f8_SOURCE_DIR}/include) - - -list( - TRANSFORM f4_library_file_names - PREPEND ${f4_SOURCE_DIR}/${lib_dir}/ - OUTPUT_VARIABLE f4_library_paths -) -list( - TRANSFORM f5_library_file_names - PREPEND ${f5_SOURCE_DIR}/${lib_dir}/ - OUTPUT_VARIABLE f5_library_paths -) -list( - TRANSFORM f6_library_file_names - PREPEND ${f6_SOURCE_DIR}/${lib_dir}/ - OUTPUT_VARIABLE f6_library_paths -) -list( - TRANSFORM f7_library_file_names - PREPEND ${f7_SOURCE_DIR}/${lib_dir}/ - OUTPUT_VARIABLE f7_library_paths -) -list( - TRANSFORM f8_library_file_names - PREPEND ${f8_SOURCE_DIR}/${lib_dir}/ - OUTPUT_VARIABLE f8_library_paths -) - -target_link_libraries( - ffmpeg4 - INTERFACE - ${f4_library_paths} -) -target_link_libraries( - ffmpeg5 - INTERFACE - ${f5_library_paths} -) -target_link_libraries( - ffmpeg6 - INTERFACE - ${f6_library_paths} -) -target_link_libraries( - ffmpeg7 - INTERFACE - ${f7_library_paths} -) -target_link_libraries( - ffmpeg8 - INTERFACE - ${f8_library_paths} -) +add_ffmpeg_target(4 "${f4_SOURCE_DIR}") +add_ffmpeg_target(5 "${f5_SOURCE_DIR}") +add_ffmpeg_target(6 "${f6_SOURCE_DIR}") +add_ffmpeg_target(7 "${f7_SOURCE_DIR}") +add_ffmpeg_target(8 "${f8_SOURCE_DIR}") diff --git a/src/torchcodec/_core/ops.py b/src/torchcodec/_core/ops.py index fa3d4e843..5b76ba8b2 100644 --- a/src/torchcodec/_core/ops.py +++ b/src/torchcodec/_core/ops.py @@ -6,7 +6,6 @@ import io import json -import os import warnings from types import ModuleType from typing import List, Optional, Tuple, Union @@ -22,8 +21,6 @@ _pybind_ops: Optional[ModuleType] = None -variant = None -core_library_path = None def load_torchcodec_shared_libraries(): # Successively try to load the shared libraries for each version of FFmpeg @@ -59,11 +56,7 @@ def load_torchcodec_shared_libraries(): _pybind_ops = _load_pybind11_module( pybind_ops_module_name, pybind_ops_library_path ) - global variant - global core_library_path - variant = ffmpeg_major_version - core_library_path = decoder_library_path - return + return ffmpeg_major_version, decoder_library_path except Exception as e: # TODO: recording and reporting exceptions this way is OK for now as it's just for debugging, # but we should probably handle that via a proper logging mechanism. @@ -89,7 +82,12 @@ def load_torchcodec_shared_libraries(): ) -load_torchcodec_shared_libraries() +# These are definitions of variables exposed as TorchCodec APIs: +# * `ffmpeg_major_version` is a major version of FFmpeg against which the loaded +# TorchCodec dynamic libraries were built. +# * `core_library_path` is a full path (including library name) to the loaded +# TorchCodec core library. +ffmpeg_major_version, core_library_path = load_torchcodec_shared_libraries() # Note: We use disallow_in_graph because PyTorch does constant propagation of diff --git a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake index 33471e002..089aa27d3 100644 --- a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake @@ -16,6 +16,7 @@ # where N is a TorchCodec variant from TORCHCODEC_VARIANTS list. include(FindPackageHandleStandardArgs) +include("${CMAKE_CURRENT_LIST_DIR}/ffmpeg_versions.cmake") # Assume we are in /share/cmake/TorchCodec/TorchCodecConfig.cmake get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) @@ -25,58 +26,17 @@ get_filename_component(TORCHCODEC_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/../. set(TORCHCODEC_INCLUDE_DIRS ${TORCHCODEC_INSTALL_PREFIX}/_core) set(TORCHCODEC_VARIANTS "") -function(add_ffmpeg_target ffmpeg_major_version libs) - set(target "torchcodec::ffmpeg${ffmpeg_major_version}") - set(prefix "TORCHCODEC_FFMPEG${ffmpeg_major_version}_INSTALL_PREFIX") - if (NOT DEFINED ENV{${prefix}}) - message("Skipping ${target} as ${prefix} is not defined") - return() - endif() - - set(prefix "$ENV{${prefix}}") - set(incdir "${prefix}/include") - if (UNIX AND NOT APPLE) - set(libdir "${prefix}/lib") - else() - message("Skipping ${target} on non-Linux platform") - return() - endif() - - set(lib_paths "") - foreach(lib IN LISTS libs) - find_library(_LIB_PATH "${lib}" PATHS "${libdir}" NO_DEFAULT_PATH) - if (NOT _LIB_PATH) - message("Skipping ${target} as ${lib} is missing") - return() - else() - list(APPEND lib_paths "${_LIB_PATH}") - endif() - # Removing _LIB_PATH from cache otherwise it won't be updated - # on the next call to find_library(). - unset(_LIB_PATH CACHE) - endforeach() - - message("Adding ${target} target") - add_library(${target} SHARED IMPORTED) - set_target_properties(${target} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${incdir} - IMPORTED_LOCATION ${lib_paths} - ) -endfunction() - function(add_torchcodec_target ffmpeg_major_version) set(target torchcodec::core${ffmpeg_major_version}) if (NOT TARGET torchcodec::ffmpeg${ffmpeg_major_version}) - message("Skipping ${target} as torchcodec::ffmpeg${ffmpeg_major_version} is not defined") - return() + message(FATAL_ERROR "torchcodec::ffmpeg${ffmpeg_major_version} target is not defined") endif() find_library(_LIB_PATH torchcodec_core${ffmpeg_major_version} PATHS "${TORCHCODEC_INSTALL_PREFIX}" NO_CACHE NO_DEFAULT_PATH) if (NOT _LIB_PATH) - message("Skipping ${target} as torchcodec_core${ffmpeg_major_version} is missing") - return() + message(FATAL_ERROR "torchcodec_core${ffmpeg_major_version} shared library is missing") endif() message("Adding ${target} target") @@ -94,92 +54,22 @@ function(add_torchcodec_target ffmpeg_major_version) set(TORCHCODEC_VARIANTS "${TORCHCODEC_VARIANTS}" PARENT_SCOPE) endfunction() -if (DEFINED ENV{TORCHCODEC_FFMPEG4_INSTALL_PREFIX} OR - DEFINED ENV{TORCHCODEC_FFMPEG5_INSTALL_PREFIX} OR - DEFINED ENV{TORCHCODEC_FFMPEG6_INSTALL_PREFIX} OR - DEFINED ENV{TORCHCODEC_FFMPEG7_INSTALL_PREFIX}) - if (UNIX AND NOT APPLE) - set(f4_library_file_names - libavutil.so.56 - libavcodec.so.58 - libavformat.so.58 - libavdevice.so.58 - libavfilter.so.7 - libswscale.so.5 - libswresample.so.3 - ) - set(f5_library_file_names - libavutil.so.57 - libavcodec.so.59 - libavformat.so.59 - libavdevice.so.59 - libavfilter.so.8 - libswscale.so.6 - libswresample.so.4 - ) - set(f6_library_file_names - libavutil.so.58 - libavcodec.so.60 - libavformat.so.60 - libavdevice.so.60 - libavfilter.so.9 - libswscale.so.7 - libswresample.so.4 - ) - set(f7_library_file_names - libavutil.so.59 - libavcodec.so.61 - libavformat.so.61 - libavdevice.so.61 - libavfilter.so.10 - libswscale.so.8 - libswresample.so.5 +set(USE_PKG_CONFIG TRUE) +foreach(ffmpeg_major_version IN LISTS TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS) + if (DEFINED ENV{TORCHCODEC_FFMPEG${ffmpeg_major_version}_INSTALL_PREFIX}) + add_ffmpeg_target( + "${ffmpeg_major_version}" + "$ENV{TORCHCODEC_FFMPEG${ffmpeg_major_version}_INSTALL_PREFIX}" ) + add_torchcodec_target(${ffmpeg_major_version}) + set(USE_PKG_CONFIG FALSE) endif() +endforeach() - add_ffmpeg_target(4 "${f4_library_file_names}") - add_ffmpeg_target(5 "${f5_library_file_names}") - add_ffmpeg_target(6 "${f6_library_file_names}") - add_ffmpeg_target(7 "${f7_library_file_names}") - - add_torchcodec_target(4) - add_torchcodec_target(5) - add_torchcodec_target(6) - add_torchcodec_target(7) -else() - find_package(PkgConfig REQUIRED) - pkg_check_modules(TORCHCODEC_LIBAV IMPORTED_TARGET - libavdevice - libavfilter - libavformat - libavcodec - libavutil - libswresample - libswscale - ) - - if (TARGET PkgConfig::TORCHCODEC_LIBAV) - # Split libavcodec's version string by '.' and convert it to a list - string(REPLACE "." ";" libavcodec_version_list ${TORCHCODEC_LIBAV_libavcodec_VERSION}) - # Get the first element of the list, which is the major version - list(GET libavcodec_version_list 0 libavcodec_major_version) - - if (${libavcodec_major_version} STREQUAL "58") - set(ffmpeg_major_version "4") - elseif (${libavcodec_major_version} STREQUAL "59") - set(ffmpeg_major_version "5") - elseif (${libavcodec_major_version} STREQUAL "60") - set(ffmpeg_major_version "6") - elseif (${libavcodec_major_version} STREQUAL "61") - set(ffmpeg_major_version "7") - endif() - - if (libavcodec_major_version) - message("Adding torchcodec::ffmpeg${ffmpeg_major_version} target") - add_library(torchcodec::ffmpeg${ffmpeg_major_version} ALIAS PkgConfig::TORCHCODEC_LIBAV) - add_torchcodec_target(${ffmpeg_major_version}) - endif() - endif() +if (USE_PKG_CONFIG) + # We will get major version of ffmpeg in `ffmpeg_major_version` variable + add_ffmpeg_target_with_pkg_config(ffmpeg_major_version) + add_torchcodec_target(${ffmpeg_major_version}) endif() find_package_handle_standard_args(TorchCodec DEFAULT_MSG TORCHCODEC_VARIANTS) diff --git a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake new file mode 100644 index 000000000..e8762bf97 --- /dev/null +++ b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake @@ -0,0 +1,277 @@ +# TorchCodec supports 2 building scenarios in the respect to compatibility with +# FFmpeg: +# +# * Building against single FFmpeg version. In this case FFmpeg libraries can +# be detected by standard pkg-config approach. +# * Building against multiple FFmpeg versions at once. In this case the goal is +# to build few shared libraries each compatible with specific FFmpeg version. +# At runtime TochCodec will check current environment and select compatible +# build of the shared library. +# +# This file contains helper definitions and functions to expose CMake FFmpeg +# targets for both scenarios described above. File defines: +# +# `TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS` +# CMake list of all FFmpeg major versions supported by TorchCodec. Note that +# this is a list of FFmpeg versions known to TorchCodec rather than a list +# of FFmpeg versions available on the current system. +# +# `add_ffmpeg_target(ffmpeg_major_version prefix)` +# * ffmpeg_major_version - FFmpeg major version for which CMake target needs +# to be defined +# * prefix - Path to the FFmpeg installation folder +# +# This function checks that required FFmpeg objects (includes and libraries) +# are actually available and defines the following target: +# * `torchcodec::ffmpeg{$ffmpeg_major_version}` +# +# `add_ffmpeg_target_with_pkg_config(ret_ffmpeg_major_version_var)` +# * `ret_ffmpeg_major_version_var` - parent scope variable where function +# will return major version of ffmpeg which was found +# +# This function searches for the FFmpeg with pkg-config and defines the +# following target: +# * `torchcodec::ffmpeg{$ffmpeg_major_version}` +# where `$ffmpeg_major_version` as major version of the detected FFmpeg. + +# All FFmpeg major versions supported by TorchCodec. +set(TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS "4;5;6;7;8") + +# Below we define FFmpeg library names we expect to have for each FFmpeg +# major version on each platform we support. +if (UNIX AND NOT APPLE) + set( + f4_library_file_names + libavutil.so.56 + libavcodec.so.58 + libavformat.so.58 + libavdevice.so.58 + libavfilter.so.7 + libswscale.so.5 + libswresample.so.3 + ) + set( + f5_library_file_names + libavutil.so.57 + libavcodec.so.59 + libavformat.so.59 + libavdevice.so.59 + libavfilter.so.8 + libswscale.so.6 + libswresample.so.4 + ) + set( + f6_library_file_names + libavutil.so.58 + libavcodec.so.60 + libavformat.so.60 + libavdevice.so.60 + libavfilter.so.9 + libswscale.so.7 + libswresample.so.4 + ) + set( + f7_library_file_names + libavutil.so.59 + libavcodec.so.61 + libavformat.so.61 + libavdevice.so.61 + libavfilter.so.10 + libswscale.so.8 + libswresample.so.5 + ) + set( + f8_library_file_names + libavutil.so.60 + libavcodec.so.62 + libavformat.so.62 + libavdevice.so.62 + libavfilter.so.11 + libswscale.so.9 + libswresample.so.6 + ) +elseif (APPLE) + set( + f4_library_file_names + libavutil.56.dylib + libavcodec.58.dylib + libavformat.58.dylib + libavdevice.58.dylib + libavfilter.7.dylib + libswscale.5.dylib + libswresample.3.dylib + ) + set( + f5_library_file_names + libavutil.57.dylib + libavcodec.59.dylib + libavformat.59.dylib + libavdevice.59.dylib + libavfilter.8.dylib + libswscale.6.dylib + libswresample.4.dylib + ) + set( + f6_library_file_names + libavutil.58.dylib + libavcodec.60.dylib + libavformat.60.dylib + libavdevice.60.dylib + libavfilter.9.dylib + libswscale.7.dylib + libswresample.4.dylib + ) + set( + f7_library_file_names + libavutil.59.dylib + libavcodec.61.dylib + libavformat.61.dylib + libavdevice.61.dylib + libavfilter.10.dylib + libswscale.8.dylib + libswresample.5.dylib + ) + set( + f8_library_file_names + libavutil.60.dylib + libavcodec.62.dylib + libavformat.62.dylib + libavdevice.62.dylib + libavfilter.11.dylib + libswscale.9.dylib + libswresample.6.dylib + ) +elseif (WIN32) + set( + f4_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f5_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f6_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f7_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f8_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) +else() + message( + FATAL_ERROR + "Unsupported operating system: ${CMAKE_SYSTEM_NAME}" + ) +endif() + +function(add_ffmpeg_target ffmpeg_major_version prefix) + # Check that given ffmpeg major version is something we support and error out if + # it's not. + list(FIND TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS "${ffmpeg_major_version}" _index) + if (_index LESS 0) + message(FATAL_ERROR "FFmpeg version ${ffmpeg_major_version} is not supported") + endif() + if (NOT DEFINED prefix) + message(FATAL_ERROR "No prefix defined calling add_ffmpeg_target()") + endif() + + set(target "torchcodec::ffmpeg${ffmpeg_major_version}") + set(incdir "${prefix}/include") + if (UNIX OR APPLE) + set(libdir "${prefix}/lib") + elseif (WIN32) + set(libdir "${prefix}/bin") + else() + message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}") + endif() + + list( + TRANSFORM f${ffmpeg_major_version}_library_file_names + PREPEND ${libdir}/ + OUTPUT_VARIABLE lib_paths + ) + + message("Adding ${target} target") + # Verify that ffmpeg includes and libraries actually exist. + foreach (path IN LISTS incdir lib_paths) + if (NOT EXISTS "${path}") + message(FATAL_ERROR "${path} does not exist") + endif() + endforeach() + + # Actually define the target + add_library(${target} INTERFACE IMPORTED) + target_include_directories(${target} INTERFACE ${incdir}) + target_link_libraries(${target} INTERFACE ${lib_paths}) +endfunction() + +function(add_ffmpeg_target_with_pkg_config ret_ffmpeg_major_version_var) + find_package(PkgConfig REQUIRED) + pkg_check_modules(TORCHCODEC_LIBAV REQUIRED IMPORTED_TARGET + libavdevice + libavfilter + libavformat + libavcodec + libavutil + libswresample + libswscale + ) + + # Split libavcodec's version string by '.' and convert it to a list + string(REPLACE "." ";" libavcodec_version_list ${TORCHCODEC_LIBAV_libavcodec_VERSION}) + # Get the first element of the list, which is the major version + list(GET libavcodec_version_list 0 libavcodec_major_version) + + if (${libavcodec_major_version} STREQUAL "58") + set(ffmpeg_major_version "4") + elseif (${libavcodec_major_version} STREQUAL "59") + set(ffmpeg_major_version "5") + elseif (${libavcodec_major_version} STREQUAL "60") + set(ffmpeg_major_version "6") + elseif (${libavcodec_major_version} STREQUAL "61") + set(ffmpeg_major_version "7") + elseif (${libavcodec_major_version} STREQUAL "62") + set(ffmpeg_major_version "8") + else() + message(FATAL_ERROR "Unsupported libavcodec version: ${libavcodec_major_version}") + endif() + + message("Adding torchcodec::ffmpeg${ffmpeg_major_version} target") + add_library(torchcodec::ffmpeg${ffmpeg_major_version} ALIAS PkgConfig::TORCHCODEC_LIBAV) + set(${ret_ffmpeg_major_version_var} ${ffmpeg_major_version} PARENT_SCOPE) +endfunction() From 3129f7e8ee4b7c5fa5ed46b7c8dca10c3a226202 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Thu, 6 Nov 2025 11:24:26 +0000 Subject: [PATCH 03/10] Move definition of LINUX var --- CMakeLists.txt | 7 +++++++ .../_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake | 7 +------ .../share/cmake/TorchCodec/ffmpeg_versions.cmake | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f2d0de2d..18dc98c01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,13 @@ cmake_minimum_required(VERSION 3.18) project(TorchCodec) +# Define LINUX platform variable globally +if (UNIX AND NOT APPLE) + set(LINUX TRUE) +else() + set(LINUX FALSE) +endif() + add_subdirectory(src/torchcodec/_core) diff --git a/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake b/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake index 17a84392c..1200499dd 100644 --- a/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +++ b/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake @@ -9,12 +9,6 @@ endif() include(FetchContent) -if (UNIX AND NOT APPLE) - set(LINUX TRUE) -else() - set(LINUX FALSE) -endif() - set( base_url https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2025-03-14 @@ -136,6 +130,7 @@ FetchContent_Declare( FetchContent_MakeAvailable(f4 f5 f6 f7 f8) +# makes add_ffmpeg_target available include("${CMAKE_CURRENT_SOURCE_DIR}/../share/cmake/TorchCodec/ffmpeg_versions.cmake") # Note: the f?_SOURCE_DIR variables were set by FetchContent_MakeAvailable diff --git a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake index e8762bf97..53f4513c0 100644 --- a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake @@ -39,7 +39,7 @@ set(TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS "4;5;6;7;8") # Below we define FFmpeg library names we expect to have for each FFmpeg # major version on each platform we support. -if (UNIX AND NOT APPLE) +if (LINUX) set( f4_library_file_names libavutil.so.56 @@ -212,7 +212,7 @@ function(add_ffmpeg_target ffmpeg_major_version prefix) set(target "torchcodec::ffmpeg${ffmpeg_major_version}") set(incdir "${prefix}/include") - if (UNIX OR APPLE) + if (LINUX OR APPLE) set(libdir "${prefix}/lib") elseif (WIN32) set(libdir "${prefix}/bin") From 0b1ec27987c858a7f905644758bea345ade28d44 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Thu, 6 Nov 2025 11:35:50 +0000 Subject: [PATCH 04/10] Nits --- .../share/cmake/TorchCodec/ffmpeg_versions.cmake | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake index 53f4513c0..369eb757a 100644 --- a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake @@ -199,6 +199,10 @@ else() ) endif() +# Create and expose torchcodec::ffmpeg${ffmpeg_major_version} target which can +# then be used as a dependency in other targets. +# prefix must be the path to the FFmpeg installation containing the usual +# `include` and `lib` directories. function(add_ffmpeg_target ffmpeg_major_version prefix) # Check that given ffmpeg major version is something we support and error out if # it's not. @@ -211,24 +215,24 @@ function(add_ffmpeg_target ffmpeg_major_version prefix) endif() set(target "torchcodec::ffmpeg${ffmpeg_major_version}") - set(incdir "${prefix}/include") + set(include_dir "${prefix}/include") if (LINUX OR APPLE) - set(libdir "${prefix}/lib") + set(lib_dir "${prefix}/lib") elseif (WIN32) - set(libdir "${prefix}/bin") + set(lib_dir "${prefix}/bin") else() message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}") endif() list( TRANSFORM f${ffmpeg_major_version}_library_file_names - PREPEND ${libdir}/ + PREPEND ${lib_dir}/ OUTPUT_VARIABLE lib_paths ) message("Adding ${target} target") # Verify that ffmpeg includes and libraries actually exist. - foreach (path IN LISTS incdir lib_paths) + foreach (path IN LISTS include_dir lib_paths) if (NOT EXISTS "${path}") message(FATAL_ERROR "${path} does not exist") endif() @@ -236,7 +240,7 @@ function(add_ffmpeg_target ffmpeg_major_version prefix) # Actually define the target add_library(${target} INTERFACE IMPORTED) - target_include_directories(${target} INTERFACE ${incdir}) + target_include_directories(${target} INTERFACE ${include_dir}) target_link_libraries(${target} INTERFACE ${lib_paths}) endfunction() From 1157487a60216a13dd7f2c492b44299685b56fde Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Thu, 6 Nov 2025 11:45:12 +0000 Subject: [PATCH 05/10] Nits - remove comment, use docstrings instead --- .../cmake/TorchCodec/ffmpeg_versions.cmake | 48 ++++--------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake index 369eb757a..1aea7cbae 100644 --- a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake @@ -1,40 +1,8 @@ -# TorchCodec supports 2 building scenarios in the respect to compatibility with -# FFmpeg: -# -# * Building against single FFmpeg version. In this case FFmpeg libraries can -# be detected by standard pkg-config approach. -# * Building against multiple FFmpeg versions at once. In this case the goal is -# to build few shared libraries each compatible with specific FFmpeg version. -# At runtime TochCodec will check current environment and select compatible -# build of the shared library. -# -# This file contains helper definitions and functions to expose CMake FFmpeg -# targets for both scenarios described above. File defines: -# -# `TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS` -# CMake list of all FFmpeg major versions supported by TorchCodec. Note that -# this is a list of FFmpeg versions known to TorchCodec rather than a list -# of FFmpeg versions available on the current system. -# -# `add_ffmpeg_target(ffmpeg_major_version prefix)` -# * ffmpeg_major_version - FFmpeg major version for which CMake target needs -# to be defined -# * prefix - Path to the FFmpeg installation folder -# -# This function checks that required FFmpeg objects (includes and libraries) -# are actually available and defines the following target: -# * `torchcodec::ffmpeg{$ffmpeg_major_version}` -# -# `add_ffmpeg_target_with_pkg_config(ret_ffmpeg_major_version_var)` -# * `ret_ffmpeg_major_version_var` - parent scope variable where function -# will return major version of ffmpeg which was found -# -# This function searches for the FFmpeg with pkg-config and defines the -# following target: -# * `torchcodec::ffmpeg{$ffmpeg_major_version}` -# where `$ffmpeg_major_version` as major version of the detected FFmpeg. +# This file exposes helpers to create and expose FFmpeg targets as torchcodec::ffmpeg${N} +# where N is the FFmpeg major version. -# All FFmpeg major versions supported by TorchCodec. +# List of FFmpeg versions that TorchCodec can support - that's not a list of +# FFmpeg versions available on the current system! set(TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS "4;5;6;7;8") # Below we define FFmpeg library names we expect to have for each FFmpeg @@ -201,8 +169,8 @@ endif() # Create and expose torchcodec::ffmpeg${ffmpeg_major_version} target which can # then be used as a dependency in other targets. -# prefix must be the path to the FFmpeg installation containing the usual -# `include` and `lib` directories. +# prefix is the path to the FFmpeg installation containing the usual `include` +# and `lib` directories. function(add_ffmpeg_target ffmpeg_major_version prefix) # Check that given ffmpeg major version is something we support and error out if # it's not. @@ -244,6 +212,9 @@ function(add_ffmpeg_target ffmpeg_major_version prefix) target_link_libraries(${target} INTERFACE ${lib_paths}) endfunction() +# Create and expose torchcodec::ffmpeg${ffmpeg_major_version} target which can +# then be used as a dependency in other targets. +# The FFmpeg installation is found by pkg-config. function(add_ffmpeg_target_with_pkg_config ret_ffmpeg_major_version_var) find_package(PkgConfig REQUIRED) pkg_check_modules(TORCHCODEC_LIBAV REQUIRED IMPORTED_TARGET @@ -257,6 +228,7 @@ function(add_ffmpeg_target_with_pkg_config ret_ffmpeg_major_version_var) ) # Split libavcodec's version string by '.' and convert it to a list + # The TORCHCODEC_LIBAV_libavcodec_VERSION is made available by pkg-config. string(REPLACE "." ";" libavcodec_version_list ${TORCHCODEC_LIBAV_libavcodec_VERSION}) # Get the first element of the list, which is the major version list(GET libavcodec_version_list 0 libavcodec_major_version) From af13e73dd6fb9d1c8b4a28987d5356a520cf3354 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Thu, 6 Nov 2025 11:51:04 +0000 Subject: [PATCH 06/10] Small refac, encapsulate stuff within add_ffmpeg_target --- .../cmake/TorchCodec/ffmpeg_versions.cmake | 199 +++--------------- 1 file changed, 34 insertions(+), 165 deletions(-) diff --git a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake index 1aea7cbae..5f4ea87c9 100644 --- a/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake @@ -1,172 +1,10 @@ # This file exposes helpers to create and expose FFmpeg targets as torchcodec::ffmpeg${N} # where N is the FFmpeg major version. -# List of FFmpeg versions that TorchCodec can support - that's not a list of -# FFmpeg versions available on the current system! +# List of FFmpeg versions that TorchCodec can support - that's not a list of +# FFmpeg versions available on the current system! set(TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS "4;5;6;7;8") -# Below we define FFmpeg library names we expect to have for each FFmpeg -# major version on each platform we support. -if (LINUX) - set( - f4_library_file_names - libavutil.so.56 - libavcodec.so.58 - libavformat.so.58 - libavdevice.so.58 - libavfilter.so.7 - libswscale.so.5 - libswresample.so.3 - ) - set( - f5_library_file_names - libavutil.so.57 - libavcodec.so.59 - libavformat.so.59 - libavdevice.so.59 - libavfilter.so.8 - libswscale.so.6 - libswresample.so.4 - ) - set( - f6_library_file_names - libavutil.so.58 - libavcodec.so.60 - libavformat.so.60 - libavdevice.so.60 - libavfilter.so.9 - libswscale.so.7 - libswresample.so.4 - ) - set( - f7_library_file_names - libavutil.so.59 - libavcodec.so.61 - libavformat.so.61 - libavdevice.so.61 - libavfilter.so.10 - libswscale.so.8 - libswresample.so.5 - ) - set( - f8_library_file_names - libavutil.so.60 - libavcodec.so.62 - libavformat.so.62 - libavdevice.so.62 - libavfilter.so.11 - libswscale.so.9 - libswresample.so.6 - ) -elseif (APPLE) - set( - f4_library_file_names - libavutil.56.dylib - libavcodec.58.dylib - libavformat.58.dylib - libavdevice.58.dylib - libavfilter.7.dylib - libswscale.5.dylib - libswresample.3.dylib - ) - set( - f5_library_file_names - libavutil.57.dylib - libavcodec.59.dylib - libavformat.59.dylib - libavdevice.59.dylib - libavfilter.8.dylib - libswscale.6.dylib - libswresample.4.dylib - ) - set( - f6_library_file_names - libavutil.58.dylib - libavcodec.60.dylib - libavformat.60.dylib - libavdevice.60.dylib - libavfilter.9.dylib - libswscale.7.dylib - libswresample.4.dylib - ) - set( - f7_library_file_names - libavutil.59.dylib - libavcodec.61.dylib - libavformat.61.dylib - libavdevice.61.dylib - libavfilter.10.dylib - libswscale.8.dylib - libswresample.5.dylib - ) - set( - f8_library_file_names - libavutil.60.dylib - libavcodec.62.dylib - libavformat.62.dylib - libavdevice.62.dylib - libavfilter.11.dylib - libswscale.9.dylib - libswresample.6.dylib - ) -elseif (WIN32) - set( - f4_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f5_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f6_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f7_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) - set( - f8_library_file_names - avutil.lib - avcodec.lib - avformat.lib - avdevice.lib - avfilter.lib - swscale.lib - swresample.lib - ) -else() - message( - FATAL_ERROR - "Unsupported operating system: ${CMAKE_SYSTEM_NAME}" - ) -endif() - # Create and expose torchcodec::ffmpeg${ffmpeg_major_version} target which can # then be used as a dependency in other targets. # prefix is the path to the FFmpeg installation containing the usual `include` @@ -182,6 +20,37 @@ function(add_ffmpeg_target ffmpeg_major_version prefix) message(FATAL_ERROR "No prefix defined calling add_ffmpeg_target()") endif() + # Define library names based on platform and FFmpeg version + if (LINUX) + if (ffmpeg_major_version EQUAL 4) + set(library_file_names libavutil.so.56 libavcodec.so.58 libavformat.so.58 libavdevice.so.58 libavfilter.so.7 libswscale.so.5 libswresample.so.3) + elseif (ffmpeg_major_version EQUAL 5) + set(library_file_names libavutil.so.57 libavcodec.so.59 libavformat.so.59 libavdevice.so.59 libavfilter.so.8 libswscale.so.6 libswresample.so.4) + elseif (ffmpeg_major_version EQUAL 6) + set(library_file_names libavutil.so.58 libavcodec.so.60 libavformat.so.60 libavdevice.so.60 libavfilter.so.9 libswscale.so.7 libswresample.so.4) + elseif (ffmpeg_major_version EQUAL 7) + set(library_file_names libavutil.so.59 libavcodec.so.61 libavformat.so.61 libavdevice.so.61 libavfilter.so.10 libswscale.so.8 libswresample.so.5) + elseif (ffmpeg_major_version EQUAL 8) + set(library_file_names libavutil.so.60 libavcodec.so.62 libavformat.so.62 libavdevice.so.62 libavfilter.so.11 libswscale.so.9 libswresample.so.6) + endif() + elseif (APPLE) + if (ffmpeg_major_version EQUAL 4) + set(library_file_names libavutil.56.dylib libavcodec.58.dylib libavformat.58.dylib libavdevice.58.dylib libavfilter.7.dylib libswscale.5.dylib libswresample.3.dylib) + elseif (ffmpeg_major_version EQUAL 5) + set(library_file_names libavutil.57.dylib libavcodec.59.dylib libavformat.59.dylib libavdevice.59.dylib libavfilter.8.dylib libswscale.6.dylib libswresample.4.dylib) + elseif (ffmpeg_major_version EQUAL 6) + set(library_file_names libavutil.58.dylib libavcodec.60.dylib libavformat.60.dylib libavdevice.60.dylib libavfilter.9.dylib libswscale.7.dylib libswresample.4.dylib) + elseif (ffmpeg_major_version EQUAL 7) + set(library_file_names libavutil.59.dylib libavcodec.61.dylib libavformat.61.dylib libavdevice.61.dylib libavfilter.10.dylib libswscale.8.dylib libswresample.5.dylib) + elseif (ffmpeg_major_version EQUAL 8) + set(library_file_names libavutil.60.dylib libavcodec.62.dylib libavformat.62.dylib libavdevice.62.dylib libavfilter.11.dylib libswscale.9.dylib libswresample.6.dylib) + endif() + elseif (WIN32) + set(library_file_names avutil.lib avcodec.lib avformat.lib avdevice.lib avfilter.lib swscale.lib swresample.lib) + else() + message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}") + endif() + set(target "torchcodec::ffmpeg${ffmpeg_major_version}") set(include_dir "${prefix}/include") if (LINUX OR APPLE) @@ -193,7 +62,7 @@ function(add_ffmpeg_target ffmpeg_major_version prefix) endif() list( - TRANSFORM f${ffmpeg_major_version}_library_file_names + TRANSFORM library_file_names PREPEND ${lib_dir}/ OUTPUT_VARIABLE lib_paths ) From ad65831dda9d4976365738e83e6e0fe108ce3123 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Thu, 6 Nov 2025 11:59:24 +0000 Subject: [PATCH 07/10] nits --- .../share/cmake/TorchCodec/TorchCodecConfig.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake index 089aa27d3..a14b6175f 100644 --- a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake @@ -5,15 +5,17 @@ # # This will define the following variables: # -# TORCHCODEC_FOUND -- True if the system has the TorchCodec library -# TORCHCODEC_VARIANTS -- List of TorchCodec variants +# TORCHCODEC_FOUND: True if the system has the TorchCodec library +# TORCHCODEC_VARIANTS: list of TorchCodec variants. A variant is a supprorted +# FFmpeg major version. # # and the following imported targets: # # torchcodec::ffmpeg${N} # torchcodec::core${N} # -# where N is a TorchCodec variant from TORCHCODEC_VARIANTS list. +# where N is a TorchCodec variant (FFmpeg major version) from +# TORCHCODEC_VARIANTS list. include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/ffmpeg_versions.cmake") From 6f42f069db3bce6f492ff400f28bb66fa4989204 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Fri, 7 Nov 2025 14:26:15 +0000 Subject: [PATCH 08/10] Nits --- .../cmake/TorchCodec/TorchCodecConfig.cmake | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake index a14b6175f..e199faa69 100644 --- a/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake +++ b/src/torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake @@ -6,7 +6,7 @@ # This will define the following variables: # # TORCHCODEC_FOUND: True if the system has the TorchCodec library -# TORCHCODEC_VARIANTS: list of TorchCodec variants. A variant is a supprorted +# TORCHCODEC_VARIANTS: list of TorchCodec variants. A variant is a supported # FFmpeg major version. # # and the following imported targets: @@ -35,9 +35,9 @@ function(add_torchcodec_target ffmpeg_major_version) message(FATAL_ERROR "torchcodec::ffmpeg${ffmpeg_major_version} target is not defined") endif() - find_library(_LIB_PATH torchcodec_core${ffmpeg_major_version} + find_library(lib_path torchcodec_core${ffmpeg_major_version} PATHS "${TORCHCODEC_INSTALL_PREFIX}" NO_CACHE NO_DEFAULT_PATH) - if (NOT _LIB_PATH) + if (NOT lib_path) message(FATAL_ERROR "torchcodec_core${ffmpeg_major_version} shared library is missing") endif() @@ -46,17 +46,17 @@ function(add_torchcodec_target ffmpeg_major_version) add_dependencies(${target} torchcodec::ffmpeg${ffmpeg_major_version}) set_target_properties(${target} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${TORCHCODEC_INCLUDE_DIRS} - IMPORTED_LOCATION ${_LIB_PATH} + IMPORTED_LOCATION ${lib_path} ) - # Removing _LIB_PATH from cache otherwise it won't be updated - # on the next call to find_library(). - unset(_LIB_PATH CACHE) list(APPEND TORCHCODEC_VARIANTS "${ffmpeg_major_version}") set(TORCHCODEC_VARIANTS "${TORCHCODEC_VARIANTS}" PARENT_SCOPE) endfunction() -set(USE_PKG_CONFIG TRUE) +# If any of the TORCHCODEC_FFMPEG${N}_INSTALL_PREFIX environment variables +# are defined, use them to locate the corresponding FFmpeg and TorchCodec targets. +# Otherwise, fall back to pkg-config to find FFmpeg. +set(use_pkg_config TRUE) foreach(ffmpeg_major_version IN LISTS TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS) if (DEFINED ENV{TORCHCODEC_FFMPEG${ffmpeg_major_version}_INSTALL_PREFIX}) add_ffmpeg_target( @@ -64,12 +64,11 @@ foreach(ffmpeg_major_version IN LISTS TORCHCODEC_SUPPORTED_FFMPEG_VERSIONS) "$ENV{TORCHCODEC_FFMPEG${ffmpeg_major_version}_INSTALL_PREFIX}" ) add_torchcodec_target(${ffmpeg_major_version}) - set(USE_PKG_CONFIG FALSE) + set(use_pkg_config FALSE) endif() endforeach() -if (USE_PKG_CONFIG) - # We will get major version of ffmpeg in `ffmpeg_major_version` variable +if (use_pkg_config) add_ffmpeg_target_with_pkg_config(ffmpeg_major_version) add_torchcodec_target(${ffmpeg_major_version}) endif() From 60b01b8969baafd579e2ff6e1b29afc296192056 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Fri, 7 Nov 2025 14:29:45 +0000 Subject: [PATCH 09/10] Nits --- src/torchcodec/_core/ops.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/torchcodec/_core/ops.py b/src/torchcodec/_core/ops.py index 5b76ba8b2..2ab9a017f 100644 --- a/src/torchcodec/_core/ops.py +++ b/src/torchcodec/_core/ops.py @@ -43,12 +43,12 @@ def load_torchcodec_shared_libraries(): exceptions = [] for ffmpeg_major_version in (8, 7, 6, 5, 4): pybind_ops_module_name = _get_pybind_ops_module_name(ffmpeg_major_version) - decoder_library_name = f"libtorchcodec_core{ffmpeg_major_version}" + core_library_name = f"libtorchcodec_core{ffmpeg_major_version}" custom_ops_library_name = f"libtorchcodec_custom_ops{ffmpeg_major_version}" pybind_ops_library_name = f"libtorchcodec_pybind_ops{ffmpeg_major_version}" try: - decoder_library_path = _get_extension_path(decoder_library_name) - torch.ops.load_library(decoder_library_path) + core_library_path = _get_extension_path(core_library_name) + torch.ops.load_library(core_library_path) torch.ops.load_library(_get_extension_path(custom_ops_library_name)) pybind_ops_library_path = _get_extension_path(pybind_ops_library_name) @@ -56,7 +56,7 @@ def load_torchcodec_shared_libraries(): _pybind_ops = _load_pybind11_module( pybind_ops_module_name, pybind_ops_library_path ) - return ffmpeg_major_version, decoder_library_path + return ffmpeg_major_version, core_library_path except Exception as e: # TODO: recording and reporting exceptions this way is OK for now as it's just for debugging, # but we should probably handle that via a proper logging mechanism. @@ -82,11 +82,6 @@ def load_torchcodec_shared_libraries(): ) -# These are definitions of variables exposed as TorchCodec APIs: -# * `ffmpeg_major_version` is a major version of FFmpeg against which the loaded -# TorchCodec dynamic libraries were built. -# * `core_library_path` is a full path (including library name) to the loaded -# TorchCodec core library. ffmpeg_major_version, core_library_path = load_torchcodec_shared_libraries() From d29eab35c5c0bbc7f6476b19e2700dcf10baee2d Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Fri, 7 Nov 2025 14:34:39 +0000 Subject: [PATCH 10/10] Nits --- src/torchcodec/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/torchcodec/__init__.py b/src/torchcodec/__init__.py index 449bb3cd0..079db9824 100644 --- a/src/torchcodec/__init__.py +++ b/src/torchcodec/__init__.py @@ -8,7 +8,6 @@ # Note: usort wants to put Frame and FrameBatch after decoders and samplers, # but that results in circular import. -from ._core import core_library_path, ffmpeg_major_version from ._frame import AudioSamples, Frame, FrameBatch # usort:skip # noqa from . import decoders, encoders, samplers # noqa @@ -18,9 +17,11 @@ except Exception: pass -# `torchcodec.cmake_prefix_path` is a Python-based way to programmatically -# obtain the correct CMAKE_PREFIX_PATH value for the TorchCodec installation. -# It can be used in a build system of a C++ application to ensure that CMake -# can successfully find TorchCodec C++ libraries. This variable is exposed -# as TorchCodec API. +# cmake_prefix_path is needed for downstream cmake-based builds that use +# torchcodec as a dependency to tell cmake where torchcodec is installed and where to find its +# CMake configuration files. +# Pytorch itself has a similar mechanism which we use in our setup.py! cmake_prefix_path = Path(__file__).parent / "share" / "cmake" +# Similarly, these are exposed for downstream builds that use torchcodec as a +# dependency. +from ._core import core_library_path, ffmpeg_major_version # usort:skip