NvCodec.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #include "pch.h"
  2. #include <absl/strings/match.h>
  3. #include <api/video_codecs/video_encoder_factory.h>
  4. #include <modules/video_coding/codecs/h264/include/h264.h>
  5. #include "Codec/CreateVideoCodecFactory.h"
  6. #include "NvCodec.h"
  7. #include "NvDecoder/NvDecoder.h"
  8. #include "NvDecoderImpl.h"
  9. #include "NvEncoder/NvEncoderCuda.h"
  10. #include "NvEncoderImpl.h"
  11. #include "ProfilerMarkerFactory.h"
  12. namespace unity
  13. {
  14. namespace webrtc
  15. {
  16. using namespace ::webrtc;
  17. constexpr char kCodecName[] = "NvCodec";
  18. class NvEncoderCudaCapability : public NvEncoderCuda
  19. {
  20. public:
  21. NvEncoderCudaCapability(CUcontext cuContext)
  22. : NvEncoderCuda(cuContext, 0, 0, NV_ENC_BUFFER_FORMAT_UNDEFINED)
  23. {
  24. }
  25. std::vector<GUID> GetEncodeProfileGUIDs(GUID encodeGUID)
  26. {
  27. uint32_t count = 0;
  28. m_nvenc.nvEncGetEncodeProfileGUIDCount(m_hEncoder, encodeGUID, &count);
  29. uint32_t validCount = 0;
  30. std::vector<GUID> guids(count);
  31. m_nvenc.nvEncGetEncodeProfileGUIDs(m_hEncoder, encodeGUID, guids.data(), count, &validCount);
  32. return guids;
  33. }
  34. int GetLevelMax(GUID encodeGUID) { return GetCapabilityValue(encodeGUID, NV_ENC_CAPS_LEVEL_MAX); }
  35. int GetLevelMin(GUID encodeGUID) { return GetCapabilityValue(encodeGUID, NV_ENC_CAPS_LEVEL_MIN); }
  36. int GetMaxWidth(GUID encodeGUID) { return GetCapabilityValue(encodeGUID, NV_ENC_CAPS_WIDTH_MAX); }
  37. int GetMaxHeight(GUID encodeGUID) { return GetCapabilityValue(encodeGUID, NV_ENC_CAPS_HEIGHT_MAX); }
  38. int GetEncoderCount(GUID encodeGUID) { return GetCapabilityValue(encodeGUID, NV_ENC_CAPS_NUM_ENCODER_ENGINES); }
  39. };
  40. int SupportedEncoderCount(CUcontext context)
  41. {
  42. auto encoder = std::make_unique<NvEncoderCudaCapability>(context);
  43. return encoder->GetEncoderCount(NV_ENC_CODEC_H264_GUID);
  44. }
  45. H264Level SupportedMaxH264Level(CUcontext context)
  46. {
  47. auto encoder = std::make_unique<NvEncoderCudaCapability>(context);
  48. int maxLevel = encoder->GetLevelMax(NV_ENC_CODEC_H264_GUID);
  49. // The max profile level supported by almost browsers is 5.2.
  50. maxLevel = std::min(maxLevel, 52);
  51. return static_cast<H264Level>(maxLevel);
  52. }
  53. std::vector<SdpVideoFormat> SupportedNvEncoderCodecs(CUcontext context)
  54. {
  55. auto encoder = std::make_unique<NvEncoderCudaCapability>(context);
  56. int maxLevel = encoder->GetLevelMax(NV_ENC_CODEC_H264_GUID);
  57. // The max profile level supported by almost browsers is 5.2.
  58. maxLevel = std::min(maxLevel, 52);
  59. H264Level supportedMaxLevel = static_cast<H264Level>(maxLevel);
  60. std::vector<GUID> profileGUIDs = encoder->GetEncodeProfileGUIDs(NV_ENC_CODEC_H264_GUID);
  61. std::vector<H264Profile> supportedProfiles;
  62. for (auto& guid : profileGUIDs)
  63. {
  64. absl::optional<H264Profile> profile = GuidToProfile(guid);
  65. if (profile.has_value())
  66. supportedProfiles.push_back(profile.value());
  67. }
  68. std::vector<SdpVideoFormat> supportedFormats;
  69. for (auto& profile : supportedProfiles)
  70. {
  71. supportedFormats.push_back(CreateH264Format(profile, supportedMaxLevel, "1"));
  72. }
  73. return supportedFormats;
  74. }
  75. static int GetCudaDeviceCapabilityMajorVersion(CUcontext context)
  76. {
  77. cuCtxSetCurrent(context);
  78. CUdevice device;
  79. cuCtxGetDevice(&device);
  80. int major;
  81. cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device);
  82. return major;
  83. }
  84. std::vector<SdpVideoFormat> SupportedNvDecoderCodecs(CUcontext context)
  85. {
  86. std::vector<SdpVideoFormat> supportedFormats;
  87. // HardwareGeneration Kepler is 3.x
  88. // https://docs.nvidia.com/deploy/cuda-compatibility/index.html#faq
  89. // Kepler support h264 profile Main, Highprofile up to Level4.1
  90. // https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide/index.html#video-decoder-capabilities__table_o3x_fms_3lb
  91. if (GetCudaDeviceCapabilityMajorVersion(context) <= 3)
  92. {
  93. supportedFormats = {
  94. CreateH264Format(webrtc::H264Profile::kProfileHigh, webrtc::H264Level::kLevel4_1, "1"),
  95. CreateH264Format(webrtc::H264Profile::kProfileMain, webrtc::H264Level::kLevel4_1, "1"),
  96. };
  97. }
  98. else
  99. {
  100. supportedFormats = {
  101. // Constrained Baseline Profile does not support NvDecoder, but WebRTC uses this profile by default,
  102. // so it must be returned in this method.
  103. CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline, webrtc::H264Level::kLevel5_1, "1"),
  104. CreateH264Format(webrtc::H264Profile::kProfileBaseline, webrtc::H264Level::kLevel5_1, "1"),
  105. CreateH264Format(webrtc::H264Profile::kProfileHigh, webrtc::H264Level::kLevel5_1, "1"),
  106. CreateH264Format(webrtc::H264Profile::kProfileMain, webrtc::H264Level::kLevel5_1, "1"),
  107. };
  108. }
  109. for (auto& format : supportedFormats)
  110. {
  111. format.parameters.emplace(kSdpKeyNameCodecImpl, kCodecName);
  112. }
  113. return supportedFormats;
  114. }
  115. std::unique_ptr<NvEncoder> NvEncoder::Create(
  116. const cricket::VideoCodec& codec,
  117. CUcontext context,
  118. CUmemorytype memoryType,
  119. NV_ENC_BUFFER_FORMAT format,
  120. ProfilerMarkerFactory* profiler)
  121. {
  122. return std::make_unique<NvEncoderImpl>(codec, context, memoryType, format, profiler);
  123. }
  124. bool NvEncoder::IsSupported()
  125. {
  126. uint32_t version = 0;
  127. uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION;
  128. NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version));
  129. if (currentVersion > version)
  130. {
  131. return false;
  132. }
  133. return true;
  134. }
  135. std::unique_ptr<NvDecoder>
  136. NvDecoder::Create(const cricket::VideoCodec& codec, CUcontext context, ProfilerMarkerFactory* profiler)
  137. {
  138. return std::make_unique<NvDecoderImpl>(context, profiler);
  139. }
  140. NvEncoderFactory::NvEncoderFactory(CUcontext context, NV_ENC_BUFFER_FORMAT format, ProfilerMarkerFactory* profiler)
  141. : context_(context)
  142. , format_(format)
  143. , profiler_(profiler)
  144. {
  145. // Some NVIDIA GPUs have a limited Encode Session count.
  146. // refer: https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new
  147. // It consumes a session to check the encoder capability.
  148. // Therefore, we check encoder capability only once in the constructor and cache it.
  149. m_cachedSupportedFormats = SupportedNvEncoderCodecs(context_);
  150. }
  151. NvEncoderFactory::~NvEncoderFactory() = default;
  152. std::vector<SdpVideoFormat> NvEncoderFactory::GetSupportedFormats() const
  153. {
  154. // If NvCodec Encoder is not supported, return empty vector.
  155. if (m_cachedSupportedFormats.empty())
  156. return std::vector<SdpVideoFormat>();
  157. // In RTCRtpTransceiver.SetCodecPreferences, the codec passed must be supported by both encoder and decoder.
  158. // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/pc/rtp_transceiver.cc;l=36
  159. // About H264, Profile and its Level must also match in this implementation.
  160. // NvEncoder supports a higher level of H264 Profile than NvDecoder.
  161. // Therefore, return the support codec of NvDecoder as Workaround.
  162. return SupportedNvDecoderCodecs(context_);
  163. }
  164. VideoEncoderFactory::CodecInfo NvEncoderFactory::QueryVideoEncoder(const SdpVideoFormat& format) const
  165. {
  166. return VideoEncoderFactory::CodecInfo();
  167. }
  168. std::unique_ptr<VideoEncoder> NvEncoderFactory::CreateVideoEncoder(const SdpVideoFormat& format)
  169. {
  170. // todo(kazuki):: add CUmemorytype::CU_MEMORYTYPE_DEVICE option
  171. return NvEncoder::Create(cricket::VideoCodec(format), context_, CU_MEMORYTYPE_ARRAY, format_, profiler_);
  172. }
  173. NvDecoderFactory::NvDecoderFactory(CUcontext context, ProfilerMarkerFactory* profiler)
  174. : context_(context)
  175. , profiler_(profiler)
  176. {
  177. }
  178. NvDecoderFactory::~NvDecoderFactory() = default;
  179. std::vector<SdpVideoFormat> NvDecoderFactory::GetSupportedFormats() const
  180. {
  181. return SupportedNvDecoderCodecs(context_);
  182. }
  183. std::unique_ptr<VideoDecoder> NvDecoderFactory::CreateVideoDecoder(const SdpVideoFormat& format)
  184. {
  185. return NvDecoder::Create(cricket::VideoCodec(format), context_, profiler_);
  186. }
  187. }
  188. }