#include "nvidia_decoder_factory.h"
#include <modules/video_coding/codecs/h264/include/h264.h>
#include <cstdlib>
#include <dlfcn.h>
#include <memory>
#include <mutex>
#include "cuda_context.h"
#include "h264_decoder_impl.h"
#include "h265_decoder_impl.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr char kDisableNvdecEnvVar[] = "LK_DISABLE_NVDEC";
bool IsNvdecDisabledByEnv() {
return std::getenv(kDisableNvdecEnvVar) != nullptr;
}
void LogNvdecDisabledByEnv() {
static std::once_flag log_once;
std::call_once(log_once, [] {
RTC_LOG(LS_INFO) << "NVIDIA NVDEC disabled because " << kDisableNvdecEnvVar
<< " is set.";
});
}
}
constexpr char kSdpKeyNameCodecImpl[] = "implementation_name";
constexpr char kCodecName[] = "NvCodec";
constexpr char kNvdecRuntimeLibrary[] = "libnvcuvid.so.1";
namespace {
bool IsNvdecRuntimeAvailable() {
void* hModule = dlopen(kNvdecRuntimeLibrary, RTLD_LAZY | RTLD_LOCAL);
if (!hModule) {
RTC_LOG(LS_WARNING) << "NVDEC runtime library (" << kNvdecRuntimeLibrary
<< ") not found, hardware decoding unavailable.";
return false;
}
dlclose(hModule);
return true;
}
}
static int GetCudaDeviceCapabilityMajorVersion(CUcontext context) {
cuCtxSetCurrent(context);
CUdevice device;
cuCtxGetDevice(&device);
int major;
cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
device);
return major;
}
std::vector<SdpVideoFormat> SupportedNvDecoderCodecs(CUcontext context) {
std::vector<SdpVideoFormat> supportedFormats;
if (GetCudaDeviceCapabilityMajorVersion(context) <= 3) {
supportedFormats = {
CreateH264Format(webrtc::H264Profile::kProfileHigh,
webrtc::H264Level::kLevel4_1, "1"),
CreateH264Format(webrtc::H264Profile::kProfileMain,
webrtc::H264Level::kLevel4_1, "1"),
};
} else {
supportedFormats = {
CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline,
webrtc::H264Level::kLevel5_1, "1"),
CreateH264Format(webrtc::H264Profile::kProfileBaseline,
webrtc::H264Level::kLevel5_1, "1"),
CreateH264Format(webrtc::H264Profile::kProfileHigh,
webrtc::H264Level::kLevel5_1, "1"),
CreateH264Format(webrtc::H264Profile::kProfileMain,
webrtc::H264Level::kLevel5_1, "1"),
SdpVideoFormat("H265"),
SdpVideoFormat("HEVC"),
};
}
for (auto& format : supportedFormats) {
format.parameters.emplace(kSdpKeyNameCodecImpl, kCodecName);
}
return supportedFormats;
}
NvidiaVideoDecoderFactory::NvidiaVideoDecoderFactory()
: cu_context_(nullptr) {
if (IsNvdecDisabledByEnv()) {
LogNvdecDisabledByEnv();
return;
}
if (!IsNvdecRuntimeAvailable()) {
RTC_LOG(LS_INFO) << "NvidiaVideoDecoderFactory created without NVDEC "
"support because the runtime library is unavailable.";
return;
}
cu_context_ = livekit_ffi::CudaContext::GetInstance();
if (cu_context_->Initialize()) {
supported_formats_ = SupportedNvDecoderCodecs(cu_context_->GetContext());
} else {
RTC_LOG(LS_ERROR) << "Failed to initialize CUDA context.";
}
RTC_LOG(LS_INFO) << "NvidiaVideoDecoderFactory created with "
<< supported_formats_.size() << " supported formats.";
}
NvidiaVideoDecoderFactory::~NvidiaVideoDecoderFactory() {}
bool NvidiaVideoDecoderFactory::IsSupported() {
if (IsNvdecDisabledByEnv()) {
LogNvdecDisabledByEnv();
return false;
}
if (!livekit_ffi::CudaContext::IsAvailable()) {
RTC_LOG(LS_WARNING) << "Cuda Context is not available.";
return false;
}
if (!IsNvdecRuntimeAvailable()) {
return false;
}
std::cout << "Nvidia Decoder is supported." << std::endl;
return true;
}
std::unique_ptr<VideoDecoder> NvidiaVideoDecoderFactory::Create(
const Environment& env,
const SdpVideoFormat& format) {
for (const auto& supported_format : supported_formats_) {
if (format.IsSameCodec(supported_format)) {
if (!cu_context_) {
cu_context_ = livekit_ffi::CudaContext::GetInstance();
if (!cu_context_->Initialize()) {
RTC_LOG(LS_ERROR) << "Failed to initialize CUDA context.";
return nullptr;
}
}
if (format.name == "H264") {
RTC_LOG(LS_INFO) << "Using NVIDIA HW decoder (NVDEC) for H264";
return std::make_unique<NvidiaH264DecoderImpl>(cu_context_->GetContext());
}
if (format.name == "H265" || format.name == "HEVC") {
RTC_LOG(LS_INFO) << "Using NVIDIA HW decoder (NVDEC) for H265/HEVC";
return std::make_unique<NvidiaH265DecoderImpl>(cu_context_->GetContext());
}
}
}
return nullptr;
}
std::vector<SdpVideoFormat> NvidiaVideoDecoderFactory::GetSupportedFormats()
const {
return supported_formats_;
}
}