torchcodec 0.10.0__cp312-cp312-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.
- torchcodec/__init__.py +27 -0
- torchcodec/_core/AVIOContextHolder.cpp +60 -0
- torchcodec/_core/AVIOContextHolder.h +64 -0
- torchcodec/_core/AVIOFileLikeContext.cpp +98 -0
- torchcodec/_core/AVIOFileLikeContext.h +55 -0
- torchcodec/_core/AVIOTensorContext.cpp +130 -0
- torchcodec/_core/AVIOTensorContext.h +44 -0
- torchcodec/_core/BetaCudaDeviceInterface.cpp +849 -0
- torchcodec/_core/BetaCudaDeviceInterface.h +196 -0
- torchcodec/_core/CMakeLists.txt +295 -0
- torchcodec/_core/CUDACommon.cpp +330 -0
- torchcodec/_core/CUDACommon.h +51 -0
- torchcodec/_core/Cache.h +124 -0
- torchcodec/_core/CpuDeviceInterface.cpp +509 -0
- torchcodec/_core/CpuDeviceInterface.h +141 -0
- torchcodec/_core/CudaDeviceInterface.cpp +602 -0
- torchcodec/_core/CudaDeviceInterface.h +79 -0
- torchcodec/_core/DeviceInterface.cpp +117 -0
- torchcodec/_core/DeviceInterface.h +191 -0
- torchcodec/_core/Encoder.cpp +1054 -0
- torchcodec/_core/Encoder.h +192 -0
- torchcodec/_core/FFMPEGCommon.cpp +684 -0
- torchcodec/_core/FFMPEGCommon.h +314 -0
- torchcodec/_core/FilterGraph.cpp +159 -0
- torchcodec/_core/FilterGraph.h +59 -0
- torchcodec/_core/Frame.cpp +47 -0
- torchcodec/_core/Frame.h +72 -0
- torchcodec/_core/Metadata.cpp +124 -0
- torchcodec/_core/Metadata.h +92 -0
- torchcodec/_core/NVCUVIDRuntimeLoader.cpp +320 -0
- torchcodec/_core/NVCUVIDRuntimeLoader.h +14 -0
- torchcodec/_core/NVDECCache.cpp +60 -0
- torchcodec/_core/NVDECCache.h +102 -0
- torchcodec/_core/SingleStreamDecoder.cpp +1586 -0
- torchcodec/_core/SingleStreamDecoder.h +391 -0
- torchcodec/_core/StreamOptions.h +70 -0
- torchcodec/_core/Transform.cpp +128 -0
- torchcodec/_core/Transform.h +86 -0
- torchcodec/_core/ValidationUtils.cpp +35 -0
- torchcodec/_core/ValidationUtils.h +21 -0
- torchcodec/_core/__init__.py +46 -0
- torchcodec/_core/_metadata.py +262 -0
- torchcodec/_core/custom_ops.cpp +1090 -0
- torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +169 -0
- torchcodec/_core/nvcuvid_include/cuviddec.h +1374 -0
- torchcodec/_core/nvcuvid_include/nvcuvid.h +610 -0
- torchcodec/_core/ops.py +605 -0
- torchcodec/_core/pybind_ops.cpp +50 -0
- torchcodec/_frame.py +146 -0
- torchcodec/_internally_replaced_utils.py +68 -0
- torchcodec/_samplers/__init__.py +7 -0
- torchcodec/_samplers/video_clip_sampler.py +419 -0
- torchcodec/decoders/__init__.py +12 -0
- torchcodec/decoders/_audio_decoder.py +185 -0
- torchcodec/decoders/_decoder_utils.py +113 -0
- torchcodec/decoders/_video_decoder.py +601 -0
- torchcodec/encoders/__init__.py +2 -0
- torchcodec/encoders/_audio_encoder.py +149 -0
- torchcodec/encoders/_video_encoder.py +196 -0
- torchcodec/libtorchcodec_core4.so +0 -0
- torchcodec/libtorchcodec_core5.so +0 -0
- torchcodec/libtorchcodec_core6.so +0 -0
- torchcodec/libtorchcodec_core7.so +0 -0
- torchcodec/libtorchcodec_core8.so +0 -0
- torchcodec/libtorchcodec_custom_ops4.so +0 -0
- torchcodec/libtorchcodec_custom_ops5.so +0 -0
- torchcodec/libtorchcodec_custom_ops6.so +0 -0
- torchcodec/libtorchcodec_custom_ops7.so +0 -0
- torchcodec/libtorchcodec_custom_ops8.so +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/libtorchcodec_pybind_ops8.so +0 -0
- torchcodec/samplers/__init__.py +2 -0
- torchcodec/samplers/_common.py +84 -0
- torchcodec/samplers/_index_based.py +287 -0
- torchcodec/samplers/_time_based.py +358 -0
- torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake +76 -0
- torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake +122 -0
- torchcodec/transforms/__init__.py +12 -0
- torchcodec/transforms/_decoder_transforms.py +375 -0
- torchcodec/version.py +2 -0
- torchcodec-0.10.0.dist-info/METADATA +286 -0
- torchcodec-0.10.0.dist-info/RECORD +88 -0
- torchcodec-0.10.0.dist-info/WHEEL +5 -0
- torchcodec-0.10.0.dist-info/licenses/LICENSE +28 -0
- torchcodec-0.10.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,196 @@
|
|
|
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
|
+
// BETA CUDA device interface that provides direct control over NVDEC
|
|
8
|
+
// while keeping FFmpeg for demuxing. A lot of the logic, particularly the use
|
|
9
|
+
// of a cache for the decoders, is inspired by DALI's implementation which is
|
|
10
|
+
// APACHE 2.0:
|
|
11
|
+
// https://github.com/NVIDIA/DALI/blob/c7539676a24a8e9e99a6e8665e277363c5445259/dali/operators/video/frames_decoder_gpu.cc#L1
|
|
12
|
+
//
|
|
13
|
+
// NVDEC / NVCUVID docs:
|
|
14
|
+
// https://docs.nvidia.com/video-technologies/video-codec-sdk/13.0/nvdec-video-decoder-api-prog-guide/index.html#using-nvidia-video-decoder-nvdecode-api
|
|
15
|
+
|
|
16
|
+
#pragma once
|
|
17
|
+
|
|
18
|
+
#include "CUDACommon.h"
|
|
19
|
+
#include "Cache.h"
|
|
20
|
+
#include "DeviceInterface.h"
|
|
21
|
+
#include "FFMPEGCommon.h"
|
|
22
|
+
#include "NVDECCache.h"
|
|
23
|
+
|
|
24
|
+
#include <map>
|
|
25
|
+
#include <memory>
|
|
26
|
+
#include <mutex>
|
|
27
|
+
#include <queue>
|
|
28
|
+
#include <unordered_map>
|
|
29
|
+
#include <vector>
|
|
30
|
+
|
|
31
|
+
#include "nvcuvid_include/cuviddec.h"
|
|
32
|
+
#include "nvcuvid_include/nvcuvid.h"
|
|
33
|
+
|
|
34
|
+
namespace facebook::torchcodec {
|
|
35
|
+
|
|
36
|
+
class BetaCudaDeviceInterface : public DeviceInterface {
|
|
37
|
+
public:
|
|
38
|
+
explicit BetaCudaDeviceInterface(const torch::Device& device);
|
|
39
|
+
virtual ~BetaCudaDeviceInterface();
|
|
40
|
+
|
|
41
|
+
void initialize(
|
|
42
|
+
const AVStream* avStream,
|
|
43
|
+
const UniqueDecodingAVFormatContext& avFormatCtx,
|
|
44
|
+
const SharedAVCodecContext& codecContext) override;
|
|
45
|
+
|
|
46
|
+
void convertAVFrameToFrameOutput(
|
|
47
|
+
UniqueAVFrame& avFrame,
|
|
48
|
+
FrameOutput& frameOutput,
|
|
49
|
+
std::optional<torch::Tensor> preAllocatedOutputTensor) override;
|
|
50
|
+
|
|
51
|
+
int sendPacket(ReferenceAVPacket& packet) override;
|
|
52
|
+
int sendEOFPacket() override;
|
|
53
|
+
int receiveFrame(UniqueAVFrame& avFrame) override;
|
|
54
|
+
void flush() override;
|
|
55
|
+
|
|
56
|
+
// NVDEC callback functions (must be public for C callbacks)
|
|
57
|
+
int streamPropertyChange(CUVIDEOFORMAT* videoFormat);
|
|
58
|
+
int frameReadyForDecoding(CUVIDPICPARAMS* picParams);
|
|
59
|
+
int frameReadyInDisplayOrder(CUVIDPARSERDISPINFO* dispInfo);
|
|
60
|
+
|
|
61
|
+
std::string getDetails() override;
|
|
62
|
+
|
|
63
|
+
private:
|
|
64
|
+
int sendCuvidPacket(CUVIDSOURCEDATAPACKET& cuvidPacket);
|
|
65
|
+
|
|
66
|
+
void initializeBSF(
|
|
67
|
+
const AVCodecParameters* codecPar,
|
|
68
|
+
const UniqueDecodingAVFormatContext& avFormatCtx);
|
|
69
|
+
// Apply bitstream filter, returns filtered packet or original if no filter
|
|
70
|
+
// needed.
|
|
71
|
+
ReferenceAVPacket& applyBSF(
|
|
72
|
+
ReferenceAVPacket& packet,
|
|
73
|
+
ReferenceAVPacket& filteredPacket);
|
|
74
|
+
|
|
75
|
+
CUdeviceptr previouslyMappedFrame_ = 0;
|
|
76
|
+
void unmapPreviousFrame();
|
|
77
|
+
|
|
78
|
+
UniqueAVFrame convertCudaFrameToAVFrame(
|
|
79
|
+
CUdeviceptr framePtr,
|
|
80
|
+
unsigned int pitch,
|
|
81
|
+
const CUVIDPARSERDISPINFO& dispInfo);
|
|
82
|
+
|
|
83
|
+
UniqueAVFrame transferCpuFrameToGpuNV12(UniqueAVFrame& cpuFrame);
|
|
84
|
+
|
|
85
|
+
CUvideoparser videoParser_ = nullptr;
|
|
86
|
+
UniqueCUvideodecoder decoder_;
|
|
87
|
+
CUVIDEOFORMAT videoFormat_ = {};
|
|
88
|
+
|
|
89
|
+
std::queue<CUVIDPARSERDISPINFO> readyFrames_;
|
|
90
|
+
|
|
91
|
+
bool eofSent_ = false;
|
|
92
|
+
|
|
93
|
+
AVRational timeBase_ = {0, 1};
|
|
94
|
+
AVRational frameRateAvgFromFFmpeg_ = {0, 1};
|
|
95
|
+
|
|
96
|
+
UniqueAVBSFContext bitstreamFilter_;
|
|
97
|
+
|
|
98
|
+
// NPP context for color conversion
|
|
99
|
+
UniqueNppContext nppCtx_;
|
|
100
|
+
|
|
101
|
+
std::unique_ptr<DeviceInterface> cpuFallback_;
|
|
102
|
+
bool nvcuvidAvailable_ = false;
|
|
103
|
+
UniqueSwsContext swsContext_;
|
|
104
|
+
SwsFrameContext prevSwsFrameContext_;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
} // namespace facebook::torchcodec
|
|
108
|
+
|
|
109
|
+
/* clang-format off */
|
|
110
|
+
// Note: [General design, sendPacket, receiveFrame, frame ordering and NVCUVID callbacks]
|
|
111
|
+
//
|
|
112
|
+
// At a high level, this decoding interface mimics the FFmpeg send/receive
|
|
113
|
+
// architecture:
|
|
114
|
+
// - sendPacket(AVPacket) sends an AVPacket from the FFmpeg demuxer to the
|
|
115
|
+
// NVCUVID parser.
|
|
116
|
+
// - receiveFrame(AVFrame) is a non-blocking call:
|
|
117
|
+
// - if a frame is ready **in display order**, it must return it. By display
|
|
118
|
+
// order, we mean that receiveFrame() must return frames with increasing pts
|
|
119
|
+
// values when called successively.
|
|
120
|
+
// - if no frame is ready, it must return AVERROR(EAGAIN) to indicate the
|
|
121
|
+
// caller should send more packets.
|
|
122
|
+
//
|
|
123
|
+
// The rest of this note assumes you have a reasonable level of familiarity with
|
|
124
|
+
// the sendPacket/receiveFrame calling pattern. If you don't, look up the core
|
|
125
|
+
// decoding loop in SingleVideoDecoder.
|
|
126
|
+
//
|
|
127
|
+
// The frame re-ordering problem:
|
|
128
|
+
// Depending on the codec and on the encoding parameters, a packet from a video
|
|
129
|
+
// stream may contain exactly one frame, more than one frame, or a fraction of a
|
|
130
|
+
// frame. And, there may be non-linear frame dependencies because of B-frames,
|
|
131
|
+
// which need both past *and* future frames to be decoded. Consider the
|
|
132
|
+
// following stream, with frames presented in display order: I0 B1 P2 B3 P4 ...
|
|
133
|
+
// - I0 is an I-frame (also key frame, can be decoded independently)
|
|
134
|
+
// - B1 is a B-frame (bi-directional) which needs both I0 and P2 to be decoded
|
|
135
|
+
// - P2 is a P-frame (predicted frame) which only needs I0 to be decodec.
|
|
136
|
+
//
|
|
137
|
+
// Because B1 needs both I0 and P2 to be properly decoded, the decode order
|
|
138
|
+
// (packet order), defined by the encoder, must be: I0 P2 B1 P4 B3 ... which is
|
|
139
|
+
// different from the display order.
|
|
140
|
+
//
|
|
141
|
+
// SendPacket(AVPacket)'s job is just to pass down the packet to the NVCUVID
|
|
142
|
+
// parser by calling cuvidParseVideoData(packet). When
|
|
143
|
+
// cuvidParseVideoData(packet) is called, it may trigger callbacks,
|
|
144
|
+
// particularly:
|
|
145
|
+
// - streamPropertyChange(videoFormat): triggered once at the start of the
|
|
146
|
+
// stream, and possibly later if the stream properties change (e.g.
|
|
147
|
+
// resolution).
|
|
148
|
+
// - frameReadyForDecoding(picParams)): triggered **in decode order** when the
|
|
149
|
+
// parser has accumulated enough data to decode a frame. We send that frame to
|
|
150
|
+
// the NVDEC hardware for **async** decoding.
|
|
151
|
+
// - frameReadyInDisplayOrder(dispInfo)): triggered **in display order** when a
|
|
152
|
+
// frame is ready to be "displayed" (returned). At that point, the parser also
|
|
153
|
+
// gives us the pts of that frame. We store (a reference to) that frame in a
|
|
154
|
+
// FIFO queue: readyFrames_.
|
|
155
|
+
//
|
|
156
|
+
// When receiveFrame(AVFrame) is called, if readyFrames_ is not empty, we pop
|
|
157
|
+
// the front of the queue, which is the next frame in display order, and map it
|
|
158
|
+
// to an AVFrame by calling cuvidMapVideoFrame(). If readyFrames_ is empty we
|
|
159
|
+
// return EAGAIN to indicate the caller should send more packets.
|
|
160
|
+
//
|
|
161
|
+
// There is potentially a small inefficiency due to the callback design: in
|
|
162
|
+
// order for us to know that a frame is ready in display order, we need the
|
|
163
|
+
// frameReadyInDisplayOrder callback to be triggered. This can only happen
|
|
164
|
+
// within cuvidParseVideoData(packet) in sendPacket(). This means there may be
|
|
165
|
+
// the following sequence of calls:
|
|
166
|
+
//
|
|
167
|
+
// sendPacket(relevantAVPacket)
|
|
168
|
+
// cuvidParseVideoData(relevantAVPacket)
|
|
169
|
+
// frameReadyForDecoding()
|
|
170
|
+
// cuvidDecodePicture() Send frame to NVDEC for async decoding
|
|
171
|
+
//
|
|
172
|
+
// receiveFrame() -> EAGAIN Frame is potentially already decoded
|
|
173
|
+
// and could be returned, but we don't
|
|
174
|
+
// know because frameReadyInDisplayOrder
|
|
175
|
+
// hasn't been triggered yet. We'll only
|
|
176
|
+
// know after sending another,
|
|
177
|
+
// potentially irrelevant packet.
|
|
178
|
+
//
|
|
179
|
+
// sendPacket(irrelevantAVPacket)
|
|
180
|
+
// cuvidParseVideoData(irrelevantAVPacket)
|
|
181
|
+
// frameReadyInDisplayOrder() Only now do we know that our target
|
|
182
|
+
// frame is ready.
|
|
183
|
+
//
|
|
184
|
+
// receiveFrame() return target frame
|
|
185
|
+
//
|
|
186
|
+
// How much this matters in practice is unclear, but probably negligible in
|
|
187
|
+
// general. Particularly when frames are decoded consecutively anyway, the
|
|
188
|
+
// "irrelevantPacket" is actually relevant for a future target frame.
|
|
189
|
+
//
|
|
190
|
+
// Note that the alternative is to *not* rely on the frameReadyInDisplayOrder
|
|
191
|
+
// callback. It's technically possible, but it would mean we now have to solve
|
|
192
|
+
// two hard, *codec-dependent* problems that the callback was solving for us:
|
|
193
|
+
// - we have to guess the frame's pts ourselves
|
|
194
|
+
// - we have to re-order the frames ourselves to preserve display order.
|
|
195
|
+
//
|
|
196
|
+
/* clang-format on */
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.18)
|
|
2
|
+
project(TorchCodec)
|
|
3
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
4
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
5
|
+
|
|
6
|
+
set(PYBIND11_FINDPYTHON ON)
|
|
7
|
+
find_package(pybind11 REQUIRED)
|
|
8
|
+
find_package(Torch REQUIRED)
|
|
9
|
+
find_package(Python3 ${PYTHON_VERSION} EXACT COMPONENTS Development)
|
|
10
|
+
|
|
11
|
+
if(DEFINED TORCHCODEC_DISABLE_COMPILE_WARNING_AS_ERROR AND TORCHCODEC_DISABLE_COMPILE_WARNING_AS_ERROR)
|
|
12
|
+
set(TORCHCODEC_WERROR_OPTION "")
|
|
13
|
+
else()
|
|
14
|
+
if (WIN32)
|
|
15
|
+
# TODO set warnings as errors on Windows as well.
|
|
16
|
+
# set(TORCHCODEC_WERROR_OPTION "/WX")
|
|
17
|
+
else()
|
|
18
|
+
set(TORCHCODEC_WERROR_OPTION "-Werror")
|
|
19
|
+
endif()
|
|
20
|
+
endif()
|
|
21
|
+
|
|
22
|
+
if (WIN32)
|
|
23
|
+
# Avoid warnings about non-ASCII characters in source files.
|
|
24
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")
|
|
25
|
+
# Important for when we add Windows CUDA: exporting all symbols is limited to
|
|
26
|
+
# 65535 symbols, which (apparently) will not work for CUDA.
|
|
27
|
+
# https://github.com/pytorch/pytorch/pull/3650
|
|
28
|
+
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
29
|
+
endif()
|
|
30
|
+
|
|
31
|
+
if (WIN32)
|
|
32
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 ${TORCHCODEC_WERROR_OPTION} ${TORCH_CXX_FLAGS}")
|
|
33
|
+
else()
|
|
34
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic ${TORCHCODEC_WERROR_OPTION} ${TORCH_CXX_FLAGS}")
|
|
35
|
+
endif()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function(make_torchcodec_sublibrary
|
|
39
|
+
library_name
|
|
40
|
+
type
|
|
41
|
+
sources
|
|
42
|
+
library_dependencies)
|
|
43
|
+
|
|
44
|
+
add_library(${library_name} ${type} ${sources})
|
|
45
|
+
set_target_properties(${library_name} PROPERTIES CXX_STANDARD 17)
|
|
46
|
+
target_include_directories(${library_name}
|
|
47
|
+
PRIVATE
|
|
48
|
+
./../../../
|
|
49
|
+
"${TORCH_INSTALL_PREFIX}/include"
|
|
50
|
+
${Python3_INCLUDE_DIRS}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Avoid adding the "lib" prefix which we already add explicitly.
|
|
54
|
+
set_target_properties(${library_name} PROPERTIES PREFIX "")
|
|
55
|
+
|
|
56
|
+
# On macOS, add Homebrew's FFmpeg library path to the rpath so that users
|
|
57
|
+
# with Homebrew-installed FFmpeg can use torchcodec without setting
|
|
58
|
+
# DYLD_LIBRARY_PATH. See https://github.com/pytorch/torchcodec/issues/570
|
|
59
|
+
if(APPLE AND NOT (DEFINED TORCHCODEC_DISABLE_HOMEBREW_RPATH AND TORCHCODEC_DISABLE_HOMEBREW_RPATH))
|
|
60
|
+
if(DEFINED ENV{HOMEBREW_PREFIX})
|
|
61
|
+
set(HOMEBREW_FFMPEG_LIB "$ENV{HOMEBREW_PREFIX}/opt/ffmpeg/lib")
|
|
62
|
+
else()
|
|
63
|
+
# Default Homebrew location on Apple Silicon
|
|
64
|
+
set(HOMEBREW_FFMPEG_LIB "/opt/homebrew/opt/ffmpeg/lib")
|
|
65
|
+
endif()
|
|
66
|
+
set_target_properties(${library_name} PROPERTIES
|
|
67
|
+
INSTALL_RPATH "${HOMEBREW_FFMPEG_LIB}"
|
|
68
|
+
)
|
|
69
|
+
endif()
|
|
70
|
+
|
|
71
|
+
target_link_libraries(
|
|
72
|
+
${library_name}
|
|
73
|
+
PUBLIC
|
|
74
|
+
${library_dependencies}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
endfunction()
|
|
78
|
+
|
|
79
|
+
function(make_torchcodec_libraries
|
|
80
|
+
ffmpeg_major_version
|
|
81
|
+
ffmpeg_target)
|
|
82
|
+
|
|
83
|
+
# We create three shared libraries per version of FFmpeg, where the version
|
|
84
|
+
# is denoted by N:
|
|
85
|
+
#
|
|
86
|
+
# 1. libtorchcodec_coreN.{ext}: Base library which contains the
|
|
87
|
+
# implementation of VideoDecoder and everything VideoDecoder needs. On
|
|
88
|
+
# Linux, {ext} is so. On Mac, it is dylib. On Windows it's dll.
|
|
89
|
+
#
|
|
90
|
+
# 2. libtorchcodec_custom_opsN.{ext}: Implementation of the PyTorch custom
|
|
91
|
+
# ops. Depends on libtorchcodec_coreN.{ext}. On Linux, {ext} is so.
|
|
92
|
+
# On Mac, it is dylib. On Windows it's dll.
|
|
93
|
+
#
|
|
94
|
+
# 3. libtorchcodec_pybind_opsN.{ext}: Implementation of the pybind11 ops. We
|
|
95
|
+
# keep these separate from the PyTorch custom ops because we have to
|
|
96
|
+
# load these libraries separately on the Python side. Depends on
|
|
97
|
+
# libtorchcodec_coreN.{ext}. On BOTH Linux and Mac {ext} is so. On
|
|
98
|
+
# Windows, it's pyd.
|
|
99
|
+
|
|
100
|
+
# 1. Create libtorchcodec_coreN.{ext}.
|
|
101
|
+
set(core_library_name "libtorchcodec_core${ffmpeg_major_version}")
|
|
102
|
+
set(core_sources
|
|
103
|
+
AVIOContextHolder.cpp
|
|
104
|
+
AVIOTensorContext.cpp
|
|
105
|
+
FFMPEGCommon.cpp
|
|
106
|
+
FilterGraph.cpp
|
|
107
|
+
Frame.cpp
|
|
108
|
+
DeviceInterface.cpp
|
|
109
|
+
CpuDeviceInterface.cpp
|
|
110
|
+
SingleStreamDecoder.cpp
|
|
111
|
+
Encoder.cpp
|
|
112
|
+
ValidationUtils.cpp
|
|
113
|
+
Transform.cpp
|
|
114
|
+
Metadata.cpp
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if(ENABLE_CUDA)
|
|
118
|
+
list(APPEND core_sources CudaDeviceInterface.cpp BetaCudaDeviceInterface.cpp NVDECCache.cpp CUDACommon.cpp NVCUVIDRuntimeLoader.cpp)
|
|
119
|
+
endif()
|
|
120
|
+
|
|
121
|
+
set(core_library_dependencies
|
|
122
|
+
${ffmpeg_target}
|
|
123
|
+
${TORCH_LIBRARIES}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if(ENABLE_CUDA)
|
|
127
|
+
list(APPEND core_library_dependencies
|
|
128
|
+
${CUDA_nppi_LIBRARY}
|
|
129
|
+
${CUDA_nppicc_LIBRARY}
|
|
130
|
+
)
|
|
131
|
+
endif()
|
|
132
|
+
|
|
133
|
+
make_torchcodec_sublibrary(
|
|
134
|
+
"${core_library_name}"
|
|
135
|
+
SHARED
|
|
136
|
+
"${core_sources}"
|
|
137
|
+
"${core_library_dependencies}"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# 2. Create libtorchcodec_custom_opsN.{ext}.
|
|
141
|
+
set(custom_ops_library_name "libtorchcodec_custom_ops${ffmpeg_major_version}")
|
|
142
|
+
set(custom_ops_sources
|
|
143
|
+
AVIOTensorContext.cpp
|
|
144
|
+
custom_ops.cpp
|
|
145
|
+
)
|
|
146
|
+
set(custom_ops_dependencies
|
|
147
|
+
${core_library_name}
|
|
148
|
+
${Python3_LIBRARIES}
|
|
149
|
+
)
|
|
150
|
+
make_torchcodec_sublibrary(
|
|
151
|
+
"${custom_ops_library_name}"
|
|
152
|
+
SHARED
|
|
153
|
+
"${custom_ops_sources}"
|
|
154
|
+
"${custom_ops_dependencies}"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# 3. Create libtorchcodec_pybind_opsN.so.
|
|
158
|
+
set(pybind_ops_library_name "libtorchcodec_pybind_ops${ffmpeg_major_version}")
|
|
159
|
+
set(pybind_ops_sources
|
|
160
|
+
AVIOFileLikeContext.cpp
|
|
161
|
+
pybind_ops.cpp
|
|
162
|
+
)
|
|
163
|
+
set(pybind_ops_dependencies
|
|
164
|
+
${core_library_name}
|
|
165
|
+
pybind11::module # This library dependency makes sure we have the right
|
|
166
|
+
# Python libraries included as well as all of the right
|
|
167
|
+
# settings so that we can successfully load the shared
|
|
168
|
+
# library as a Python module on Mac. If we instead use
|
|
169
|
+
# ${Python3_LIBRARIES}, it works on Linux but not on
|
|
170
|
+
# Mac.
|
|
171
|
+
)
|
|
172
|
+
make_torchcodec_sublibrary(
|
|
173
|
+
"${pybind_ops_library_name}"
|
|
174
|
+
MODULE # Note that this not SHARED; otherwise we build the wrong kind
|
|
175
|
+
# of library on Mac. On Mac, SHARED becomes .dylib and MODULE becomes
|
|
176
|
+
# a .so. We want pybind11 libraries to become .so. If this is
|
|
177
|
+
# changed to SHARED, we will be able to succesfully compile a
|
|
178
|
+
# .dylib, but we will not be able to succesfully import that as
|
|
179
|
+
# a Python module on Mac.
|
|
180
|
+
"${pybind_ops_sources}"
|
|
181
|
+
"${pybind_ops_dependencies}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if(WIN32)
|
|
185
|
+
# On Windows, we need to set the suffix to .pyd so that Python can
|
|
186
|
+
# import the shared library as a module. Just setting the MODULE type
|
|
187
|
+
# isn't enough.
|
|
188
|
+
set_target_properties(${pybind_ops_library_name} PROPERTIES SUFFIX ".pyd")
|
|
189
|
+
endif()
|
|
190
|
+
|
|
191
|
+
# pybind11 limits the visibility of symbols in the shared library to prevent
|
|
192
|
+
# stray initialization of py::objects. The rest of the object code must
|
|
193
|
+
# match. See:
|
|
194
|
+
# https://pybind11.readthedocs.io/en/stable/faq.html#someclass-declared-with-greater-visibility-than-the-type-of-its-field-someclass-member-wattributes
|
|
195
|
+
# We have to do this for both pybind_ops and custom_ops because they include
|
|
196
|
+
# some of the same headers.
|
|
197
|
+
#
|
|
198
|
+
# Note that this is Linux only. It's not necessary on Windows, and on Mac
|
|
199
|
+
# hiding visibility can actually break dyanmic casts across share libraries
|
|
200
|
+
# because the type infos don't get exported.
|
|
201
|
+
if(LINUX)
|
|
202
|
+
target_compile_options(
|
|
203
|
+
${pybind_ops_library_name}
|
|
204
|
+
PUBLIC
|
|
205
|
+
"-fvisibility=hidden"
|
|
206
|
+
)
|
|
207
|
+
target_compile_options(
|
|
208
|
+
${custom_ops_library_name}
|
|
209
|
+
PUBLIC
|
|
210
|
+
"-fvisibility=hidden"
|
|
211
|
+
)
|
|
212
|
+
endif()
|
|
213
|
+
|
|
214
|
+
# The value we use here must match the value we return from
|
|
215
|
+
# _get_pybind_ops_module_name() on the Python side. If the values do not
|
|
216
|
+
# match, then we will be unable to import the C++ shared library as a
|
|
217
|
+
# Python module at runtime.
|
|
218
|
+
target_compile_definitions(
|
|
219
|
+
${pybind_ops_library_name}
|
|
220
|
+
PRIVATE
|
|
221
|
+
PYBIND_OPS_MODULE_NAME=core_pybind_ops
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if(APPLE)
|
|
225
|
+
# If we don't make sure this flag is set, we run into segfauls at import
|
|
226
|
+
# time on Mac. See:
|
|
227
|
+
# https://github.com/pybind/pybind11/issues/3907#issuecomment-1170412764
|
|
228
|
+
target_link_options(
|
|
229
|
+
${pybind_ops_library_name}
|
|
230
|
+
PUBLIC
|
|
231
|
+
"LINKER:-undefined,dynamic_lookup"
|
|
232
|
+
)
|
|
233
|
+
endif()
|
|
234
|
+
|
|
235
|
+
# Install all libraries.
|
|
236
|
+
set(
|
|
237
|
+
all_libraries
|
|
238
|
+
${core_library_name}
|
|
239
|
+
${custom_ops_library_name}
|
|
240
|
+
${pybind_ops_library_name}
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# The install step is invoked within CMakeBuild.build_library() in
|
|
244
|
+
# setup.py and just copies the built files from the temp
|
|
245
|
+
# cmake/setuptools build folder into the CMAKE_INSTALL_PREFIX folder. We
|
|
246
|
+
# still need to manually pass "DESTINATION ..." for cmake to copy those
|
|
247
|
+
# files in CMAKE_INSTALL_PREFIX instead of CMAKE_INSTALL_PREFIX/lib.
|
|
248
|
+
install(
|
|
249
|
+
TARGETS ${all_libraries}
|
|
250
|
+
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}
|
|
251
|
+
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} # For Windows
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
endfunction()
|
|
255
|
+
|
|
256
|
+
if(DEFINED ENV{BUILD_AGAINST_ALL_FFMPEG_FROM_S3})
|
|
257
|
+
message(
|
|
258
|
+
STATUS
|
|
259
|
+
"Building and dynamically linking libtorchcodec against our pre-built
|
|
260
|
+
non-GPL FFmpeg libraries. These libraries are only used at build time,
|
|
261
|
+
you still need a different FFmpeg to be installed for run time!"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# This will expose the torchcodec::ffmpeg{N} (N=4,5,6,7,8) targets
|
|
265
|
+
include(
|
|
266
|
+
${CMAKE_CURRENT_SOURCE_DIR}/fetch_and_expose_non_gpl_ffmpeg_libs.cmake
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
make_torchcodec_libraries(8 torchcodec::ffmpeg8)
|
|
270
|
+
make_torchcodec_libraries(7 torchcodec::ffmpeg7)
|
|
271
|
+
make_torchcodec_libraries(6 torchcodec::ffmpeg6)
|
|
272
|
+
make_torchcodec_libraries(4 torchcodec::ffmpeg4)
|
|
273
|
+
make_torchcodec_libraries(5 torchcodec::ffmpeg5)
|
|
274
|
+
else()
|
|
275
|
+
message(
|
|
276
|
+
STATUS
|
|
277
|
+
"Building and dynamically linking libtorchcodec against the installed
|
|
278
|
+
FFmpeg libraries. This require pkg-config to be installed. If you have
|
|
279
|
+
installed FFmpeg from conda, make sure pkg-config is installed from
|
|
280
|
+
conda as well."
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# This will expose `add_ffmpeg_target_with_pkg_config`
|
|
284
|
+
include("${CMAKE_CURRENT_SOURCE_DIR}/../share/cmake/TorchCodec/ffmpeg_versions.cmake")
|
|
285
|
+
|
|
286
|
+
add_ffmpeg_target_with_pkg_config(ffmpeg_major_version)
|
|
287
|
+
make_torchcodec_libraries(${ffmpeg_major_version} torchcodec::ffmpeg${ffmpeg_major_version})
|
|
288
|
+
|
|
289
|
+
# Expose these values updwards so that the test compilation does not need
|
|
290
|
+
# to re-figure it out. FIXME: it's not great that we just copy-paste the
|
|
291
|
+
# library names.
|
|
292
|
+
set(libtorchcodec_library_name "libtorchcodec_core${ffmpeg_major_version}" PARENT_SCOPE)
|
|
293
|
+
set(libtorchcodec_custom_ops_name "libtorchcodec_custom_ops${ffmpeg_major_version}" PARENT_SCOPE)
|
|
294
|
+
set(libav_include_dirs ${LIBAV_INCLUDE_DIRS} PARENT_SCOPE)
|
|
295
|
+
endif()
|