jim-nvenc-helpers.c 4.8 KB
Newer Older
1 2 3
#include "jim-nvenc.h"
#include <util/platform.h>
#include <util/threading.h>
4
#include <util/dstr.h>
5 6 7 8 9 10

static void *nvenc_lib = NULL;
static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER};
NV_CREATE_INSTANCE_FUNC nv_create_instance = NULL;

J
jp9000 已提交
11
#define error(format, ...) blog(LOG_ERROR, "[jim-nvenc] " format, ##__VA_ARGS__)
12

13 14
bool nv_failed(obs_encoder_t *encoder, NVENCSTATUS err, const char *func,
	       const char *call)
15
{
16 17 18 19
	struct dstr error_message = {0};

	switch (err) {
	case NV_ENC_SUCCESS:
20 21
		return false;

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
	case NV_ENC_ERR_OUT_OF_MEMORY:
		obs_encoder_set_last_error(
			encoder,
			"NVENC Error: Too many concurrent sessions. "
			"Try closing other recording software which might "
			"be using NVENC such as Windows 10 Game DVR.");
		break;

	case NV_ENC_ERR_UNSUPPORTED_DEVICE:
		obs_encoder_set_last_error(
			encoder,
			"NVENC Error: Unsupported device. Check your "
			"video card supports NVENC and that the drivers are "
			"up to date.");
		break;

	default:
		dstr_printf(&error_message,
			    "NVENC Error: %s: %s failed: %d (%s)", func, call,
			    (int)err, nv_error_name(err));
		obs_encoder_set_last_error(encoder, error_message.array);
		dstr_free(&error_message);
		break;
	}

47
	error("%s: %s failed: %d (%s)", func, call, (int)err,
J
jp9000 已提交
48
	      nv_error_name(err));
49 50 51
	return true;
}

52
#define NV_FAILED(e, x) nv_failed(e, x, __FUNCTION__, #x)
53

54 55
bool load_nvenc_lib(void)
{
J
jp9000 已提交
56
	if (sizeof(void *) == 8) {
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
		nvenc_lib = os_dlopen("nvEncodeAPI64.dll");
	} else {
		nvenc_lib = os_dlopen("nvEncodeAPI.dll");
	}

	return !!nvenc_lib;
}

static void *load_nv_func(const char *func)
{
	void *func_ptr = os_dlsym(nvenc_lib, func);
	if (!func_ptr) {
		error("Could not load function: %s", func);
	}
	return func_ptr;
}

J
jp9000 已提交
74
typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *);
75 76 77 78

const char *nv_error_name(NVENCSTATUS err)
{
#define RETURN_CASE(x) \
J
jp9000 已提交
79 80
	case x:        \
		return #x
81 82

	switch (err) {
J
jp9000 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
		RETURN_CASE(NV_ENC_SUCCESS);
		RETURN_CASE(NV_ENC_ERR_NO_ENCODE_DEVICE);
		RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_DEVICE);
		RETURN_CASE(NV_ENC_ERR_INVALID_ENCODERDEVICE);
		RETURN_CASE(NV_ENC_ERR_INVALID_DEVICE);
		RETURN_CASE(NV_ENC_ERR_DEVICE_NOT_EXIST);
		RETURN_CASE(NV_ENC_ERR_INVALID_PTR);
		RETURN_CASE(NV_ENC_ERR_INVALID_EVENT);
		RETURN_CASE(NV_ENC_ERR_INVALID_PARAM);
		RETURN_CASE(NV_ENC_ERR_INVALID_CALL);
		RETURN_CASE(NV_ENC_ERR_OUT_OF_MEMORY);
		RETURN_CASE(NV_ENC_ERR_ENCODER_NOT_INITIALIZED);
		RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_PARAM);
		RETURN_CASE(NV_ENC_ERR_LOCK_BUSY);
		RETURN_CASE(NV_ENC_ERR_NOT_ENOUGH_BUFFER);
		RETURN_CASE(NV_ENC_ERR_INVALID_VERSION);
		RETURN_CASE(NV_ENC_ERR_MAP_FAILED);
		RETURN_CASE(NV_ENC_ERR_NEED_MORE_INPUT);
		RETURN_CASE(NV_ENC_ERR_ENCODER_BUSY);
		RETURN_CASE(NV_ENC_ERR_EVENT_NOT_REGISTERD);
		RETURN_CASE(NV_ENC_ERR_GENERIC);
		RETURN_CASE(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY);
		RETURN_CASE(NV_ENC_ERR_UNIMPLEMENTED);
		RETURN_CASE(NV_ENC_ERR_RESOURCE_REGISTER_FAILED);
		RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_REGISTERED);
		RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_MAPPED);
109 110 111 112 113 114
	}
#undef RETURN_CASE

	return "Unknown Error";
}

115
static inline bool init_nvenc_internal(obs_encoder_t *encoder)
116 117 118 119 120 121 122 123
{
	static bool initialized = false;
	static bool success = false;

	if (initialized)
		return success;
	initialized = true;

J
jp9000 已提交
124 125
	NV_MAX_VER_FUNC nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func(
		"NvEncodeAPIGetMaxSupportedVersion");
126
	if (!nv_max_ver) {
127 128 129 130
		obs_encoder_set_last_error(
			encoder,
			"Missing NvEncodeAPIGetMaxSupportedVersion, check "
			"your video card drivers are up to date.");
131 132 133 134
		return false;
	}

	uint32_t ver = 0;
135
	if (NV_FAILED(encoder, nv_max_ver(&ver))) {
136 137 138
		return false;
	}

J
jp9000 已提交
139 140
	uint32_t cur_ver = (NVENCAPI_MAJOR_VERSION << 4) |
			   NVENCAPI_MINOR_VERSION;
141
	if (cur_ver > ver) {
142 143 144 145 146
		obs_encoder_set_last_error(
			encoder,
			"Your current video card driver does not support "
			"this NVENC version, please update your drivers.");

147
		error("Current driver version does not support this NVENC "
J
jp9000 已提交
148
		      "version, please upgrade your driver");
149 150 151
		return false;
	}

J
jp9000 已提交
152 153
	nv_create_instance = (NV_CREATE_INSTANCE_FUNC)load_nv_func(
		"NvEncodeAPICreateInstance");
154
	if (!nv_create_instance) {
155 156 157
		obs_encoder_set_last_error(
			encoder, "Missing NvEncodeAPICreateInstance, check "
				 "your video card drivers are up to date.");
158 159 160
		return false;
	}

161
	if (NV_FAILED(encoder, nv_create_instance(&nv))) {
162 163 164 165 166 167 168
		return false;
	}

	success = true;
	return true;
}

169
bool init_nvenc(obs_encoder_t *encoder)
170 171 172 173
{
	bool success;

	pthread_mutex_lock(&init_mutex);
174
	success = init_nvenc_internal(encoder);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	pthread_mutex_unlock(&init_mutex);

	return success;
}

extern struct obs_encoder_info nvenc_info;

void jim_nvenc_load(void)
{
	pthread_mutex_init(&init_mutex, NULL);
	obs_register_encoder(&nvenc_info);
}

void jim_nvenc_unload(void)
{
	pthread_mutex_destroy(&init_mutex);
}