jim-nvenc-helpers.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #include "jim-nvenc.h"
  2. #include <util/platform.h>
  3. #include <util/threading.h>
  4. #include <util/dstr.h>
  5. static void *nvenc_lib = NULL;
  6. static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
  7. NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER};
  8. NV_CREATE_INSTANCE_FUNC nv_create_instance = NULL;
  9. #define error(format, ...) blog(LOG_ERROR, "[jim-nvenc] " format, ##__VA_ARGS__)
  10. bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...)
  11. {
  12. struct dstr message = {0};
  13. struct dstr error_message = {0};
  14. va_list args;
  15. va_start(args, format);
  16. dstr_vprintf(&message, format, args);
  17. va_end(args);
  18. dstr_printf(&error_message, "NVENC Error: %s", message.array);
  19. obs_encoder_set_last_error(encoder, error_message.array);
  20. error("%s", error_message.array);
  21. dstr_free(&error_message);
  22. dstr_free(&message);
  23. return true;
  24. }
  25. bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err,
  26. const char *func, const char *call)
  27. {
  28. struct dstr error_message = {0};
  29. switch (err) {
  30. case NV_ENC_SUCCESS:
  31. return false;
  32. case NV_ENC_ERR_OUT_OF_MEMORY:
  33. obs_encoder_set_last_error(
  34. encoder, obs_module_text("NVENC.TooManySessions"));
  35. break;
  36. case NV_ENC_ERR_UNSUPPORTED_DEVICE:
  37. obs_encoder_set_last_error(
  38. encoder, obs_module_text("NVENC.UnsupportedDevice"));
  39. break;
  40. case NV_ENC_ERR_INVALID_VERSION:
  41. obs_encoder_set_last_error(
  42. encoder, obs_module_text("NVENC.OutdatedDriver"));
  43. break;
  44. default:
  45. dstr_printf(&error_message,
  46. "NVENC Error: %s: %s failed: %d (%s)", func, call,
  47. (int)err, nv_error_name(err));
  48. obs_encoder_set_last_error(encoder, error_message.array);
  49. dstr_free(&error_message);
  50. break;
  51. }
  52. if (session) {
  53. error("%s: %s failed: %d (%s): %s", func, call, (int)err,
  54. nv_error_name(err), nv.nvEncGetLastErrorString(session));
  55. } else {
  56. error("%s: %s failed: %d (%s)", func, call, (int)err,
  57. nv_error_name(err));
  58. }
  59. return true;
  60. }
  61. #define NV_FAILED(e, x) nv_failed2(e, NULL, x, __FUNCTION__, #x)
  62. bool load_nvenc_lib(void)
  63. {
  64. const char *const file = (sizeof(void *) == 8) ? "nvEncodeAPI64.dll"
  65. : "nvEncodeAPI.dll";
  66. nvenc_lib = os_dlopen(file);
  67. return nvenc_lib != NULL;
  68. }
  69. static void *load_nv_func(const char *func)
  70. {
  71. void *func_ptr = os_dlsym(nvenc_lib, func);
  72. if (!func_ptr) {
  73. error("Could not load function: %s", func);
  74. }
  75. return func_ptr;
  76. }
  77. typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *);
  78. const char *nv_error_name(NVENCSTATUS err)
  79. {
  80. #define RETURN_CASE(x) \
  81. case x: \
  82. return #x
  83. switch (err) {
  84. RETURN_CASE(NV_ENC_SUCCESS);
  85. RETURN_CASE(NV_ENC_ERR_NO_ENCODE_DEVICE);
  86. RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_DEVICE);
  87. RETURN_CASE(NV_ENC_ERR_INVALID_ENCODERDEVICE);
  88. RETURN_CASE(NV_ENC_ERR_INVALID_DEVICE);
  89. RETURN_CASE(NV_ENC_ERR_DEVICE_NOT_EXIST);
  90. RETURN_CASE(NV_ENC_ERR_INVALID_PTR);
  91. RETURN_CASE(NV_ENC_ERR_INVALID_EVENT);
  92. RETURN_CASE(NV_ENC_ERR_INVALID_PARAM);
  93. RETURN_CASE(NV_ENC_ERR_INVALID_CALL);
  94. RETURN_CASE(NV_ENC_ERR_OUT_OF_MEMORY);
  95. RETURN_CASE(NV_ENC_ERR_ENCODER_NOT_INITIALIZED);
  96. RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_PARAM);
  97. RETURN_CASE(NV_ENC_ERR_LOCK_BUSY);
  98. RETURN_CASE(NV_ENC_ERR_NOT_ENOUGH_BUFFER);
  99. RETURN_CASE(NV_ENC_ERR_INVALID_VERSION);
  100. RETURN_CASE(NV_ENC_ERR_MAP_FAILED);
  101. RETURN_CASE(NV_ENC_ERR_NEED_MORE_INPUT);
  102. RETURN_CASE(NV_ENC_ERR_ENCODER_BUSY);
  103. RETURN_CASE(NV_ENC_ERR_EVENT_NOT_REGISTERD);
  104. RETURN_CASE(NV_ENC_ERR_GENERIC);
  105. RETURN_CASE(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY);
  106. RETURN_CASE(NV_ENC_ERR_UNIMPLEMENTED);
  107. RETURN_CASE(NV_ENC_ERR_RESOURCE_REGISTER_FAILED);
  108. RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_REGISTERED);
  109. RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_MAPPED);
  110. }
  111. #undef RETURN_CASE
  112. return "Unknown Error";
  113. }
  114. static inline bool init_nvenc_internal(obs_encoder_t *encoder)
  115. {
  116. static bool initialized = false;
  117. static bool success = false;
  118. if (initialized)
  119. return success;
  120. initialized = true;
  121. NV_MAX_VER_FUNC nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func(
  122. "NvEncodeAPIGetMaxSupportedVersion");
  123. if (!nv_max_ver) {
  124. obs_encoder_set_last_error(
  125. encoder,
  126. "Missing NvEncodeAPIGetMaxSupportedVersion, check "
  127. "your video card drivers are up to date.");
  128. return false;
  129. }
  130. uint32_t ver = 0;
  131. if (NV_FAILED(encoder, nv_max_ver(&ver))) {
  132. return false;
  133. }
  134. uint32_t cur_ver = (NVENCAPI_MAJOR_VERSION << 4) |
  135. NVENCAPI_MINOR_VERSION;
  136. if (cur_ver > ver) {
  137. obs_encoder_set_last_error(
  138. encoder, obs_module_text("NVENC.OutdatedDriver"));
  139. error("Current driver version does not support this NVENC "
  140. "version, please upgrade your driver");
  141. return false;
  142. }
  143. nv_create_instance = (NV_CREATE_INSTANCE_FUNC)load_nv_func(
  144. "NvEncodeAPICreateInstance");
  145. if (!nv_create_instance) {
  146. obs_encoder_set_last_error(
  147. encoder, "Missing NvEncodeAPICreateInstance, check "
  148. "your video card drivers are up to date.");
  149. return false;
  150. }
  151. if (NV_FAILED(encoder, nv_create_instance(&nv))) {
  152. return false;
  153. }
  154. success = true;
  155. return true;
  156. }
  157. bool init_nvenc(obs_encoder_t *encoder)
  158. {
  159. bool success;
  160. pthread_mutex_lock(&init_mutex);
  161. success = init_nvenc_internal(encoder);
  162. pthread_mutex_unlock(&init_mutex);
  163. return success;
  164. }
  165. extern struct obs_encoder_info h264_nvenc_info;
  166. #ifdef ENABLE_HEVC
  167. extern struct obs_encoder_info hevc_nvenc_info;
  168. #endif
  169. void jim_nvenc_load(bool h264, bool hevc)
  170. {
  171. pthread_mutex_init(&init_mutex, NULL);
  172. if (h264)
  173. obs_register_encoder(&h264_nvenc_info);
  174. #ifdef ENABLE_HEVC
  175. if (hevc)
  176. obs_register_encoder(&hevc_nvenc_info);
  177. #endif
  178. }
  179. void jim_nvenc_unload(void)
  180. {
  181. pthread_mutex_destroy(&init_mutex);
  182. }