obs-ffmpeg.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include <obs-module.h>
  2. #include <util/darray.h>
  3. #include <util/platform.h>
  4. #include <libavutil/log.h>
  5. #include <libavcodec/avcodec.h>
  6. #include <libavformat/avformat.h>
  7. #include <pthread.h>
  8. #ifndef __APPLE__
  9. #include "dynlink_cuda.h"
  10. #include "nvEncodeAPI.h"
  11. #endif
  12. #define NVENC_CAP 0x30
  13. OBS_DECLARE_MODULE()
  14. OBS_MODULE_USE_DEFAULT_LOCALE("obs-ffmpeg", "en-US")
  15. extern struct obs_source_info ffmpeg_source;
  16. extern struct obs_output_info ffmpeg_output;
  17. extern struct obs_output_info ffmpeg_muxer;
  18. extern struct obs_output_info replay_buffer;
  19. extern struct obs_encoder_info aac_encoder_info;
  20. extern struct obs_encoder_info opus_encoder_info;
  21. extern struct obs_encoder_info nvenc_encoder_info;
  22. static DARRAY(struct log_context {
  23. void *context;
  24. char str[4096];
  25. int print_prefix;
  26. } *) active_log_contexts;
  27. static DARRAY(struct log_context *) cached_log_contexts;
  28. pthread_mutex_t log_contexts_mutex = PTHREAD_MUTEX_INITIALIZER;
  29. static struct log_context *create_or_fetch_log_context(void *context)
  30. {
  31. pthread_mutex_lock(&log_contexts_mutex);
  32. for (size_t i = 0; i < active_log_contexts.num; i++) {
  33. if (context == active_log_contexts.array[i]->context) {
  34. pthread_mutex_unlock(&log_contexts_mutex);
  35. return active_log_contexts.array[i];
  36. }
  37. }
  38. struct log_context *new_log_context = NULL;
  39. size_t cnt = cached_log_contexts.num;
  40. if (!!cnt) {
  41. new_log_context = cached_log_contexts.array[cnt - 1];
  42. da_pop_back(cached_log_contexts);
  43. }
  44. if (!new_log_context)
  45. new_log_context = bzalloc(sizeof(struct log_context));
  46. new_log_context->context = context;
  47. new_log_context->str[0] = '\0';
  48. new_log_context->print_prefix = 1;
  49. da_push_back(active_log_contexts, &new_log_context);
  50. pthread_mutex_unlock(&log_contexts_mutex);
  51. return new_log_context;
  52. }
  53. static void destroy_log_context(struct log_context *log_context)
  54. {
  55. pthread_mutex_lock(&log_contexts_mutex);
  56. da_erase_item(active_log_contexts, &log_context);
  57. da_push_back(cached_log_contexts, &log_context);
  58. pthread_mutex_unlock(&log_contexts_mutex);
  59. }
  60. static void ffmpeg_log_callback(void* context, int level, const char* format,
  61. va_list args)
  62. {
  63. if (format == NULL)
  64. return;
  65. struct log_context *log_context = create_or_fetch_log_context(context);
  66. char *str = log_context->str;
  67. av_log_format_line(context, level, format, args, str + strlen(str),
  68. (int)(sizeof(log_context->str) - strlen(str)),
  69. &log_context->print_prefix);
  70. int obs_level;
  71. switch (level) {
  72. case AV_LOG_PANIC:
  73. case AV_LOG_FATAL:
  74. obs_level = LOG_ERROR;
  75. break;
  76. case AV_LOG_ERROR:
  77. case AV_LOG_WARNING:
  78. obs_level = LOG_WARNING;
  79. break;
  80. case AV_LOG_INFO:
  81. case AV_LOG_VERBOSE:
  82. obs_level = LOG_INFO;
  83. break;
  84. case AV_LOG_DEBUG:
  85. default:
  86. obs_level = LOG_DEBUG;
  87. }
  88. if (!log_context->print_prefix)
  89. return;
  90. char *str_end = str + strlen(str) - 1;
  91. while(str < str_end) {
  92. if (*str_end != '\n')
  93. break;
  94. *str_end-- = '\0';
  95. }
  96. if (str_end <= str)
  97. goto cleanup;
  98. blog(obs_level, "[ffmpeg] %s", str);
  99. cleanup:
  100. destroy_log_context(log_context);
  101. }
  102. #ifndef __APPLE__
  103. static const char *nvenc_check_name = "nvenc_check";
  104. static inline bool push_context_(tcuCtxPushCurrent_v2 *cuCtxPushCurrent,
  105. CUcontext context)
  106. {
  107. return cuCtxPushCurrent(context) == CUDA_SUCCESS;
  108. }
  109. static inline bool pop_context_(tcuCtxPopCurrent_v2 *cuCtxPopCurrent)
  110. {
  111. CUcontext dummy;
  112. return cuCtxPopCurrent(&dummy) == CUDA_SUCCESS;
  113. }
  114. #define push_context(context) push_context_(cuCtxPushCurrent, context)
  115. #define pop_context() pop_context_(cuCtxPopCurrent)
  116. typedef NVENCSTATUS (NVENCAPI *NVENCODEAPICREATEINSTANCE)(
  117. NV_ENCODE_API_FUNCTION_LIST *functionList);
  118. static bool nvenc_supported(void)
  119. {
  120. av_register_all();
  121. profile_start(nvenc_check_name);
  122. AVCodec *nvenc = avcodec_find_encoder_by_name("nvenc_h264");
  123. void *lib = NULL;
  124. void *cudalib = NULL;
  125. bool success = false;
  126. if (!nvenc) {
  127. goto cleanup;
  128. }
  129. #if defined(_WIN32)
  130. if (sizeof(void*) == 8) {
  131. lib = os_dlopen("nvEncodeAPI64.dll");
  132. } else {
  133. lib = os_dlopen("nvEncodeAPI.dll");
  134. }
  135. cudalib = os_dlopen("nvcuda.dll");
  136. #else
  137. lib = os_dlopen("libnvidia-encode.so.1");
  138. cudalib = os_dlopen("libcuda.so.1");
  139. #endif
  140. if (!lib || !cudalib) {
  141. goto cleanup;
  142. }
  143. /* ------------------------------------------- */
  144. CUdevice device;
  145. CUcontext context;
  146. CUresult cu_result;
  147. void *nvencoder = NULL;
  148. NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = {0};
  149. NV_ENCODE_API_FUNCTION_LIST nv = {0};
  150. GUID *guids = NULL;
  151. int nv_result;
  152. int count;
  153. #define GET_CUDA_FUNC(func) \
  154. t ## func *func = os_dlsym(cudalib, #func); \
  155. do { \
  156. if (!func) { \
  157. goto cleanup; \
  158. } \
  159. } while (false)
  160. GET_CUDA_FUNC(cuInit);
  161. GET_CUDA_FUNC(cuDeviceGet);
  162. GET_CUDA_FUNC(cuDeviceComputeCapability);
  163. #undef GET_CUDA_FUNC
  164. #define GET_CUDA_V2_FUNC(func) \
  165. t ## func ## _v2 *func = os_dlsym(cudalib, #func "_v2"); \
  166. do { \
  167. if (!func) { \
  168. goto cleanup; \
  169. } \
  170. } while (false)
  171. GET_CUDA_V2_FUNC(cuCtxCreate);
  172. GET_CUDA_V2_FUNC(cuCtxDestroy);
  173. GET_CUDA_V2_FUNC(cuCtxPushCurrent);
  174. GET_CUDA_V2_FUNC(cuCtxPopCurrent);
  175. #undef GET_CUDA_V2_FUNC
  176. NVENCODEAPICREATEINSTANCE create_instance = os_dlsym(lib,
  177. "NvEncodeAPICreateInstance");
  178. if (!create_instance) {
  179. goto cleanup;
  180. }
  181. nv.version = NV_ENCODE_API_FUNCTION_LIST_VER;
  182. nv_result = create_instance(&nv);
  183. if (nv_result != NV_ENC_SUCCESS) {
  184. goto cleanup;
  185. }
  186. cu_result = cuInit(0);
  187. if (cu_result != CUDA_SUCCESS) {
  188. goto cleanup;
  189. }
  190. cu_result = cuDeviceGet(&device, 0);
  191. if (cu_result != CUDA_SUCCESS) {
  192. goto cleanup;
  193. }
  194. int major, minor;
  195. cu_result = cuDeviceComputeCapability(&major, &minor, device);
  196. if (cu_result != CUDA_SUCCESS) {
  197. goto cleanup;
  198. }
  199. if (((major << 4) | minor) < NVENC_CAP) {
  200. goto cleanup;
  201. }
  202. cu_result = cuCtxCreate(&context, 0, device);
  203. if (cu_result != CUDA_SUCCESS) {
  204. goto cleanup;
  205. }
  206. if (!pop_context()) {
  207. goto cleanup2;
  208. }
  209. params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
  210. params.apiVersion = NVENCAPI_VERSION;
  211. params.device = context;
  212. params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
  213. nv_result = nv.nvEncOpenEncodeSessionEx(&params, &nvencoder);
  214. if (nv_result != NV_ENC_SUCCESS) {
  215. nvencoder = NULL;
  216. goto cleanup2;
  217. }
  218. nv_result = nv.nvEncGetEncodeGUIDCount(nvencoder, &count);
  219. if (nv_result != NV_ENC_SUCCESS || !count) {
  220. goto cleanup3;
  221. }
  222. guids = bzalloc(count * sizeof(GUID));
  223. nv_result = nv.nvEncGetEncodeGUIDs(nvencoder, guids, count, &count);
  224. if (nv_result != NV_ENC_SUCCESS || !count) {
  225. goto cleanup3;
  226. }
  227. for (int i = 0; i < count; i++) {
  228. int ret = memcmp(&guids[i], &NV_ENC_CODEC_H264_GUID,
  229. sizeof(*guids));
  230. if (ret == 0) {
  231. success = true;
  232. break;
  233. }
  234. }
  235. cleanup3:
  236. bfree(guids);
  237. if (nvencoder && push_context(context)) {
  238. nv.nvEncDestroyEncoder(nvencoder);
  239. pop_context();
  240. }
  241. cleanup2:
  242. cuCtxDestroy(context);
  243. cleanup:
  244. os_dlclose(lib);
  245. os_dlclose(cudalib);
  246. profile_end(nvenc_check_name);
  247. return success;
  248. }
  249. #endif
  250. bool obs_module_load(void)
  251. {
  252. da_init(active_log_contexts);
  253. da_init(cached_log_contexts);
  254. //av_log_set_callback(ffmpeg_log_callback);
  255. obs_register_source(&ffmpeg_source);
  256. obs_register_output(&ffmpeg_output);
  257. obs_register_output(&ffmpeg_muxer);
  258. obs_register_output(&replay_buffer);
  259. obs_register_encoder(&aac_encoder_info);
  260. obs_register_encoder(&opus_encoder_info);
  261. #ifndef __APPLE__
  262. if (nvenc_supported()) {
  263. blog(LOG_INFO, "NVENC supported");
  264. obs_register_encoder(&nvenc_encoder_info);
  265. }
  266. #endif
  267. return true;
  268. }
  269. void obs_module_unload(void)
  270. {
  271. av_log_set_callback(av_log_default_callback);
  272. #ifdef _WIN32
  273. pthread_mutex_destroy(&log_contexts_mutex);
  274. #endif
  275. for (size_t i = 0; i < active_log_contexts.num; i++) {
  276. bfree(active_log_contexts.array[i]);
  277. }
  278. for (size_t i = 0; i < cached_log_contexts.num; i++) {
  279. bfree(cached_log_contexts.array[i]);
  280. }
  281. da_free(active_log_contexts);
  282. da_free(cached_log_contexts);
  283. }