Skip to content

Commit de573db

Browse files
committed
WIP
1 parent 75a3325 commit de573db

File tree

6 files changed

+242
-24
lines changed

6 files changed

+242
-24
lines changed

src/torchcodec/_core/BetaCudaDeviceInterface.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
#include "src/torchcodec/_core/NVDECCache.h"
1717

1818
// #include <cuda_runtime.h> // For cudaStreamSynchronize
19+
20+
// Include dynamic loader for interface
21+
#include "src/torchcodec/_core/NVCUVIDDynamicLoader.h"
22+
23+
// Include NVCUVID headers for types
1924
#include "src/torchcodec/_core/nvcuvid_include/cuviddec.h"
2025
#include "src/torchcodec/_core/nvcuvid_include/nvcuvid.h"
2126

@@ -155,6 +160,12 @@ std::optional<cudaVideoCodec> validateCodecSupport(AVCodecID codecId) {
155160
bool nativeNVDECSupport(const SharedAVCodecContext& codecContext) {
156161
// Return true iff the input video stream is supported by our NVDEC
157162
// implementation.
163+
164+
// First check if NVCUVID is available
165+
if (!isNVCUVIDLoaded()) {
166+
return false;
167+
}
168+
158169
auto codecType = validateCodecSupport(codecContext->codec_id);
159170
if (!codecType.has_value()) {
160171
return false;
@@ -222,6 +233,14 @@ BetaCudaDeviceInterface::BetaCudaDeviceInterface(const torch::Device& device)
222233

223234
initializeCudaContextWithPytorch(device_);
224235
nppCtx_ = getNppStreamContext(device_);
236+
237+
// Try to load NVCUVID - if this fails, we'll use CPU fallback
238+
if (!initNVCUVID()) {
239+
// NVCUVID loading failed, we'll create CPU fallback during initialize()
240+
nvcuvidAvailable_ = false;
241+
} else {
242+
nvcuvidAvailable_ = true;
243+
}
225244
}
226245

227246
BetaCudaDeviceInterface::~BetaCudaDeviceInterface() {
@@ -249,7 +268,7 @@ void BetaCudaDeviceInterface::initialize(
249268
const AVStream* avStream,
250269
const UniqueDecodingAVFormatContext& avFormatCtx,
251270
[[maybe_unused]] const SharedAVCodecContext& codecContext) {
252-
if (!nativeNVDECSupport(codecContext)) {
271+
if (!nvcuvidAvailable_ || !nativeNVDECSupport(codecContext)) {
253272
cpuFallback_ = createDeviceInterface(torch::kCPU);
254273
TORCH_CHECK(
255274
cpuFallback_ != nullptr, "Failed to create CPU device interface");
@@ -552,7 +571,7 @@ int BetaCudaDeviceInterface::receiveFrame(UniqueAVFrame& avFrame) {
552571
// SingleStreamDecoder. Either way, the underlying output surface can be
553572
// safely re-used.
554573
unmapPreviousFrame();
555-
CUresult result = cuvidMapVideoFrame(
574+
CUresult result = cuvidMapVideoFrame64(
556575
*decoder_.get(), dispInfo.picture_index, &framePtr, &pitch, &procParams);
557576
if (result != CUDA_SUCCESS) {
558577
return AVERROR_EXTERNAL;
@@ -569,7 +588,7 @@ void BetaCudaDeviceInterface::unmapPreviousFrame() {
569588
return;
570589
}
571590
CUresult result =
572-
cuvidUnmapVideoFrame(*decoder_.get(), previouslyMappedFrame_);
591+
cuvidUnmapVideoFrame64(*decoder_.get(), previouslyMappedFrame_);
573592
TORCH_CHECK(
574593
result == CUDA_SUCCESS, "Failed to unmap previous frame: ", result);
575594
previouslyMappedFrame_ = 0;
@@ -700,8 +719,15 @@ void BetaCudaDeviceInterface::convertAVFrameToFrameOutput(
700719
}
701720

702721
std::string BetaCudaDeviceInterface::getDetails() {
703-
return std::string("Beta CUDA Device Interface. Using ") +
704-
(cpuFallback_ ? "CPU fallback." : "NVDEC.");
722+
if (cpuFallback_) {
723+
if (!nvcuvidAvailable_) {
724+
return "Beta CUDA Device Interface. Using CPU fallback (NVCUVID not available - install NVIDIA drivers with NVDEC support).";
725+
} else {
726+
return "Beta CUDA Device Interface. Using CPU fallback.";
727+
}
728+
} else {
729+
return "Beta CUDA Device Interface. Using NVDEC.";
730+
}
705731
}
706732

707733
} // namespace facebook::torchcodec

src/torchcodec/_core/BetaCudaDeviceInterface.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ class BetaCudaDeviceInterface : public DeviceInterface {
9898
UniqueNppContext nppCtx_;
9999

100100
std::unique_ptr<DeviceInterface> cpuFallback_;
101+
102+
// Track whether NVCUVID was successfully loaded
103+
bool nvcuvidAvailable_ = false;
101104
};
102105

103106
} // namespace facebook::torchcodec

src/torchcodec/_core/CMakeLists.txt

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function(make_torchcodec_libraries
9999
)
100100

101101
if(ENABLE_CUDA)
102-
list(APPEND core_sources CudaDeviceInterface.cpp BetaCudaDeviceInterface.cpp NVDECCache.cpp CUDACommon.cpp)
102+
list(APPEND core_sources CudaDeviceInterface.cpp BetaCudaDeviceInterface.cpp NVDECCache.cpp CUDACommon.cpp NVCUVIDDynamicWrappers.cpp)
103103
endif()
104104

105105
set(core_library_dependencies
@@ -108,27 +108,9 @@ function(make_torchcodec_libraries
108108
)
109109

110110
if(ENABLE_CUDA)
111-
# Try to find NVCUVID. Try the normal way first. This should work locally.
112-
find_library(NVCUVID_LIBRARY NAMES nvcuvid)
113-
# If not found, try with version suffix, or hardcoded path. Appears
114-
# to be necessary on the CI.
115-
if(NOT NVCUVID_LIBRARY)
116-
find_library(NVCUVID_LIBRARY NAMES nvcuvid.1 PATHS /usr/lib64 /usr/lib)
117-
endif()
118-
if(NOT NVCUVID_LIBRARY)
119-
set(NVCUVID_LIBRARY "/usr/lib64/libnvcuvid.so.1")
120-
endif()
121-
122-
if(NVCUVID_LIBRARY)
123-
message(STATUS "Found NVCUVID: ${NVCUVID_LIBRARY}")
124-
else()
125-
message(FATAL_ERROR "Could not find NVCUVID library")
126-
endif()
127-
128111
list(APPEND core_library_dependencies
129112
${CUDA_nppi_LIBRARY}
130113
${CUDA_nppicc_LIBRARY}
131-
${NVCUVID_LIBRARY}
132114
)
133115
endif()
134116

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
#pragma once
8+
9+
namespace facebook::torchcodec {
10+
11+
// Simple interface for dynamic NVCUVID loading
12+
bool initNVCUVID();
13+
bool isNVCUVIDLoaded();
14+
15+
} // namespace facebook::torchcodec
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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 "src/torchcodec/_core/NVCUVIDDynamicLoader.h"
8+
9+
// Include NVCUVID headers to get types
10+
#include "src/torchcodec/_core/nvcuvid_include/cuviddec.h"
11+
#include "src/torchcodec/_core/nvcuvid_include/nvcuvid.h"
12+
13+
#include <dlfcn.h>
14+
#include <torch/types.h>
15+
#include <cstdio>
16+
#include <mutex>
17+
18+
namespace facebook::torchcodec {
19+
20+
// Function typedefs
21+
typedef CUresult CUDAAPI
22+
tcuvidCreateVideoParser(CUvideoparser* pObj, CUVIDPARSERPARAMS* pParams);
23+
typedef CUresult CUDAAPI
24+
tcuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET* pPacket);
25+
typedef CUresult CUDAAPI tcuvidDestroyVideoParser(CUvideoparser obj);
26+
typedef CUresult CUDAAPI tcuvidGetDecoderCaps(CUVIDDECODECAPS* pdc);
27+
typedef CUresult CUDAAPI
28+
tcuvidCreateDecoder(CUvideodecoder* phDecoder, CUVIDDECODECREATEINFO* pdci);
29+
typedef CUresult CUDAAPI tcuvidDestroyDecoder(CUvideodecoder hDecoder);
30+
typedef CUresult CUDAAPI
31+
tcuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS* pPicParams);
32+
typedef CUresult CUDAAPI tcuvidMapVideoFrame64(
33+
CUvideodecoder hDecoder,
34+
int nPicIdx,
35+
unsigned long long* pDevPtr,
36+
unsigned int* pPitch,
37+
CUVIDPROCPARAMS* pVPP);
38+
typedef CUresult CUDAAPI
39+
tcuvidUnmapVideoFrame64(CUvideodecoder hDecoder, unsigned long long DevPtr);
40+
41+
// Global function pointers
42+
static tcuvidCreateVideoParser* _dlcuvidCreateVideoParser = nullptr;
43+
static tcuvidParseVideoData* _dlcuvidParseVideoData = nullptr;
44+
static tcuvidDestroyVideoParser* _dlcuvidDestroyVideoParser = nullptr;
45+
static tcuvidGetDecoderCaps* _dlcuvidGetDecoderCaps = nullptr;
46+
static tcuvidCreateDecoder* _dlcuvidCreateDecoder = nullptr;
47+
static tcuvidDestroyDecoder* _dlcuvidDestroyDecoder = nullptr;
48+
static tcuvidDecodePicture* _dlcuvidDecodePicture = nullptr;
49+
static tcuvidMapVideoFrame64* _dlcuvidMapVideoFrame64 = nullptr;
50+
static tcuvidUnmapVideoFrame64* _dlcuvidUnmapVideoFrame64 = nullptr;
51+
52+
static void* g_nvcuvid_handle = nullptr;
53+
static std::mutex g_nvcuvid_mutex;
54+
55+
template <typename T>
56+
T* loadFunction(const char* functionName) {
57+
return reinterpret_cast<T*>(dlsym(g_nvcuvid_handle, functionName));
58+
}
59+
60+
bool initNVCUVID() {
61+
std::lock_guard<std::mutex> lock(g_nvcuvid_mutex);
62+
63+
if (g_nvcuvid_handle != nullptr) {
64+
return true; // Already loaded
65+
}
66+
67+
g_nvcuvid_handle = dlopen("libnvcuvid.so", RTLD_NOW);
68+
if (g_nvcuvid_handle == nullptr) {
69+
g_nvcuvid_handle = dlopen("libnvcuvid.so.1", RTLD_NOW);
70+
if (g_nvcuvid_handle == nullptr) {
71+
return false;
72+
}
73+
}
74+
75+
// Load all function pointers
76+
_dlcuvidCreateVideoParser =
77+
loadFunction<tcuvidCreateVideoParser>("cuvidCreateVideoParser");
78+
_dlcuvidParseVideoData =
79+
loadFunction<tcuvidParseVideoData>("cuvidParseVideoData");
80+
_dlcuvidDestroyVideoParser =
81+
loadFunction<tcuvidDestroyVideoParser>("cuvidDestroyVideoParser");
82+
_dlcuvidGetDecoderCaps =
83+
loadFunction<tcuvidGetDecoderCaps>("cuvidGetDecoderCaps");
84+
_dlcuvidCreateDecoder =
85+
loadFunction<tcuvidCreateDecoder>("cuvidCreateDecoder");
86+
_dlcuvidDestroyDecoder =
87+
loadFunction<tcuvidDestroyDecoder>("cuvidDestroyDecoder");
88+
_dlcuvidDecodePicture =
89+
loadFunction<tcuvidDecodePicture>("cuvidDecodePicture");
90+
_dlcuvidMapVideoFrame64 =
91+
loadFunction<tcuvidMapVideoFrame64>("cuvidMapVideoFrame64");
92+
_dlcuvidUnmapVideoFrame64 =
93+
loadFunction<tcuvidUnmapVideoFrame64>("cuvidUnmapVideoFrame64");
94+
95+
// Check if all critical functions loaded successfully
96+
return (_dlcuvidCreateVideoParser && _dlcuvidParseVideoData &&
97+
_dlcuvidDestroyVideoParser && _dlcuvidGetDecoderCaps &&
98+
_dlcuvidCreateDecoder && _dlcuvidDestroyDecoder &&
99+
_dlcuvidDecodePicture && _dlcuvidMapVideoFrame64 &&
100+
_dlcuvidUnmapVideoFrame64);
101+
}
102+
103+
bool isNVCUVIDLoaded() {
104+
return g_nvcuvid_handle != nullptr && _dlcuvidCreateDecoder != nullptr;
105+
}
106+
107+
108+
} // namespace facebook::torchcodec
109+
110+
// Provide C-style wrapper functions that replace the original NVCUVID functions
111+
extern "C" {
112+
113+
CUresult CUDAAPI
114+
cuvidCreateVideoParser(CUvideoparser* pObj, CUVIDPARSERPARAMS* pParams) {
115+
TORCH_CHECK(
116+
facebook::torchcodec::_dlcuvidCreateVideoParser,
117+
"cuvidCreateVideoParser called but NVCUVID not loaded!");
118+
return facebook::torchcodec::_dlcuvidCreateVideoParser(pObj, pParams);
119+
}
120+
121+
CUresult CUDAAPI
122+
cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET* pPacket) {
123+
TORCH_CHECK(
124+
facebook::torchcodec::_dlcuvidParseVideoData,
125+
"cuvidParseVideoData called but NVCUVID not loaded!");
126+
return facebook::torchcodec::_dlcuvidParseVideoData(obj, pPacket);
127+
}
128+
129+
CUresult CUDAAPI cuvidDestroyVideoParser(CUvideoparser obj) {
130+
TORCH_CHECK(
131+
facebook::torchcodec::_dlcuvidDestroyVideoParser,
132+
"cuvidDestroyVideoParser called but NVCUVID not loaded!");
133+
return facebook::torchcodec::_dlcuvidDestroyVideoParser(obj);
134+
}
135+
136+
CUresult CUDAAPI cuvidGetDecoderCaps(CUVIDDECODECAPS* pdc) {
137+
TORCH_CHECK(
138+
facebook::torchcodec::_dlcuvidGetDecoderCaps,
139+
"cuvidGetDecoderCaps called but NVCUVID not loaded!");
140+
return facebook::torchcodec::_dlcuvidGetDecoderCaps(pdc);
141+
}
142+
143+
CUresult CUDAAPI
144+
cuvidCreateDecoder(CUvideodecoder* phDecoder, CUVIDDECODECREATEINFO* pdci) {
145+
TORCH_CHECK(
146+
facebook::torchcodec::_dlcuvidCreateDecoder,
147+
"cuvidCreateDecoder called but NVCUVID not loaded!");
148+
return facebook::torchcodec::_dlcuvidCreateDecoder(phDecoder, pdci);
149+
}
150+
151+
CUresult CUDAAPI cuvidDestroyDecoder(CUvideodecoder hDecoder) {
152+
TORCH_CHECK(
153+
facebook::torchcodec::_dlcuvidDestroyDecoder,
154+
"cuvidDestroyDecoder called but NVCUVID not loaded!");
155+
return facebook::torchcodec::_dlcuvidDestroyDecoder(hDecoder);
156+
}
157+
158+
CUresult CUDAAPI
159+
cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS* pPicParams) {
160+
TORCH_CHECK(
161+
facebook::torchcodec::_dlcuvidDecodePicture,
162+
"cuvidDecodePicture called but NVCUVID not loaded!");
163+
return facebook::torchcodec::_dlcuvidDecodePicture(hDecoder, pPicParams);
164+
}
165+
166+
CUresult CUDAAPI cuvidMapVideoFrame64(
167+
CUvideodecoder hDecoder,
168+
int nPicIdx,
169+
unsigned long long* pDevPtr,
170+
unsigned int* pPitch,
171+
CUVIDPROCPARAMS* pVPP) {
172+
TORCH_CHECK(
173+
facebook::torchcodec::_dlcuvidMapVideoFrame64,
174+
"cuvidMapVideoFrame64 called but NVCUVID not loaded!");
175+
return facebook::torchcodec::_dlcuvidMapVideoFrame64(
176+
hDecoder, nPicIdx, pDevPtr, pPitch, pVPP);
177+
}
178+
179+
CUresult CUDAAPI
180+
cuvidUnmapVideoFrame64(CUvideodecoder hDecoder, unsigned long long DevPtr) {
181+
TORCH_CHECK(
182+
facebook::torchcodec::_dlcuvidUnmapVideoFrame64,
183+
"cuvidUnmapVideoFrame64 called but NVCUVID not loaded!");
184+
return facebook::torchcodec::_dlcuvidUnmapVideoFrame64(hDecoder, DevPtr);
185+
}
186+
187+
} // extern "C"

src/torchcodec/_core/NVDECCache.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
#include <cuda.h>
1414
#include <torch/types.h>
15+
16+
// Include dynamic loader for interface
17+
#include "src/torchcodec/_core/NVCUVIDDynamicLoader.h"
18+
19+
// Include NVCUVID headers for types
1520
#include "src/torchcodec/_core/nvcuvid_include/cuviddec.h"
1621
#include "src/torchcodec/_core/nvcuvid_include/nvcuvid.h"
1722

0 commit comments

Comments
 (0)