vaapi-utils.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // SPDX-FileCopyrightText: 2022 tytan652 <[email protected]>
  2. //
  3. // SPDX-License-Identifier: GPL-2.0-or-later
  4. #include "vaapi-utils.h"
  5. #include <util/bmem.h>
  6. #include <util/dstr.h>
  7. #include <va/va_drm.h>
  8. #include <va/va_str.h>
  9. #include <stdio.h>
  10. #include <unistd.h>
  11. #include <fcntl.h>
  12. static bool version_logged = false;
  13. inline static VADisplay vaapi_open_display_drm(int *fd, const char *device_path)
  14. {
  15. VADisplay va_dpy;
  16. if (!device_path)
  17. return NULL;
  18. *fd = open(device_path, O_RDWR);
  19. if (*fd < 0) {
  20. blog(LOG_ERROR, "VAAPI: Failed to open device '%s'",
  21. device_path);
  22. return NULL;
  23. }
  24. va_dpy = vaGetDisplayDRM(*fd);
  25. if (!va_dpy) {
  26. blog(LOG_ERROR, "VAAPI: Failed to initialize DRM display");
  27. return NULL;
  28. }
  29. return va_dpy;
  30. }
  31. inline static void vaapi_close_display_drm(int *fd)
  32. {
  33. if (*fd < 0)
  34. return;
  35. close(*fd);
  36. *fd = -1;
  37. }
  38. static void vaapi_log_info_cb(void *user_context, const char *message)
  39. {
  40. UNUSED_PARAMETER(user_context);
  41. // Libva message always ends with a newline
  42. struct dstr m;
  43. dstr_init_copy(&m, message);
  44. dstr_depad(&m);
  45. blog(LOG_DEBUG, "Libva: %s", m.array);
  46. dstr_free(&m);
  47. }
  48. static void vaapi_log_error_cb(void *user_context, const char *message)
  49. {
  50. UNUSED_PARAMETER(user_context);
  51. // Libva message always ends with a newline
  52. struct dstr m;
  53. dstr_init_copy(&m, message);
  54. dstr_depad(&m);
  55. blog(LOG_DEBUG, "Libva error: %s", m.array);
  56. dstr_free(&m);
  57. }
  58. VADisplay vaapi_open_device(int *fd, const char *device_path,
  59. const char *func_name)
  60. {
  61. VADisplay va_dpy;
  62. VAStatus va_status;
  63. int major, minor;
  64. const char *driver;
  65. va_dpy = vaapi_open_display_drm(fd, device_path);
  66. if (!va_dpy)
  67. return NULL;
  68. blog(LOG_DEBUG, "VAAPI: Initializing display in %s", func_name);
  69. vaSetInfoCallback(va_dpy, vaapi_log_info_cb, NULL);
  70. vaSetErrorCallback(va_dpy, vaapi_log_error_cb, NULL);
  71. va_status = vaInitialize(va_dpy, &major, &minor);
  72. if (va_status != VA_STATUS_SUCCESS) {
  73. blog(LOG_ERROR, "VAAPI: Failed to initialize display in %s",
  74. func_name);
  75. vaapi_close_device(fd, va_dpy);
  76. return NULL;
  77. }
  78. blog(LOG_DEBUG, "VAAPI: Display initialized");
  79. if (!version_logged) {
  80. blog(LOG_INFO, "VAAPI: API version %d.%d", major, minor);
  81. version_logged = true;
  82. }
  83. driver = vaQueryVendorString(va_dpy);
  84. blog(LOG_DEBUG, "VAAPI: '%s' in use for device '%s'", driver,
  85. device_path);
  86. return va_dpy;
  87. }
  88. void vaapi_close_device(int *fd, VADisplay dpy)
  89. {
  90. vaTerminate(dpy);
  91. vaapi_close_display_drm(fd);
  92. }
  93. static uint32_t vaapi_display_ep_combo_rate_controls(VAProfile profile,
  94. VAEntrypoint entrypoint,
  95. VADisplay dpy,
  96. const char *device_path)
  97. {
  98. VAStatus va_status;
  99. VAConfigAttrib attrib[1];
  100. attrib->type = VAConfigAttribRateControl;
  101. va_status = vaGetConfigAttributes(dpy, profile, entrypoint, attrib, 1);
  102. switch (va_status) {
  103. case VA_STATUS_SUCCESS:
  104. return attrib->value;
  105. case VA_STATUS_ERROR_UNSUPPORTED_PROFILE:
  106. blog(LOG_DEBUG, "VAAPI: %s is not supported by the device '%s'",
  107. vaProfileStr(profile), device_path);
  108. return 0;
  109. case VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT:
  110. blog(LOG_DEBUG,
  111. "VAAPI: %s %s is not supported by the device '%s'",
  112. vaProfileStr(profile), vaEntrypointStr(entrypoint),
  113. device_path);
  114. return 0;
  115. default:
  116. blog(LOG_ERROR,
  117. "VAAPI: Fail to get RC attribute from the %s %s of the device '%s'",
  118. vaProfileStr(profile), vaEntrypointStr(entrypoint),
  119. device_path);
  120. return 0;
  121. }
  122. }
  123. static bool vaapi_display_ep_combo_supported(VAProfile profile,
  124. VAEntrypoint entrypoint,
  125. VADisplay dpy,
  126. const char *device_path)
  127. {
  128. uint32_t ret = vaapi_display_ep_combo_rate_controls(profile, entrypoint,
  129. dpy, device_path);
  130. if (ret & VA_RC_CBR || ret & VA_RC_CQP || ret & VA_RC_VBR)
  131. return true;
  132. return false;
  133. }
  134. bool vaapi_device_rc_supported(VAProfile profile, VADisplay dpy, uint32_t rc,
  135. const char *device_path)
  136. {
  137. uint32_t ret = vaapi_display_ep_combo_rate_controls(
  138. profile, VAEntrypointEncSlice, dpy, device_path);
  139. if (ret & rc)
  140. return true;
  141. ret = vaapi_display_ep_combo_rate_controls(
  142. profile, VAEntrypointEncSliceLP, dpy, device_path);
  143. if (ret & rc)
  144. return true;
  145. return false;
  146. }
  147. static bool vaapi_display_ep_bframe_supported(VAProfile profile,
  148. VAEntrypoint entrypoint,
  149. VADisplay dpy)
  150. {
  151. VAStatus va_status;
  152. VAConfigAttrib attrib[1];
  153. attrib->type = VAConfigAttribEncMaxRefFrames;
  154. va_status = vaGetConfigAttributes(dpy, profile, entrypoint, attrib, 1);
  155. if (va_status == VA_STATUS_SUCCESS &&
  156. attrib->value != VA_ATTRIB_NOT_SUPPORTED)
  157. return attrib->value >> 16;
  158. return false;
  159. }
  160. bool vaapi_device_bframe_supported(VAProfile profile, VADisplay dpy)
  161. {
  162. bool ret = vaapi_display_ep_bframe_supported(profile,
  163. VAEntrypointEncSlice, dpy);
  164. if (ret)
  165. return true;
  166. ret = vaapi_display_ep_bframe_supported(profile, VAEntrypointEncSliceLP,
  167. dpy);
  168. if (ret)
  169. return true;
  170. return false;
  171. }
  172. #define CHECK_PROFILE(ret, profile, va_dpy, device_path) \
  173. if (vaapi_display_ep_combo_supported(profile, VAEntrypointEncSlice, \
  174. va_dpy, device_path)) { \
  175. blog(LOG_DEBUG, "'%s' support encoding with %s", device_path, \
  176. vaProfileStr(profile)); \
  177. ret |= true; \
  178. }
  179. #define CHECK_PROFILE_LP(ret, profile, va_dpy, device_path) \
  180. if (vaapi_display_ep_combo_supported(profile, VAEntrypointEncSliceLP, \
  181. va_dpy, device_path)) { \
  182. blog(LOG_DEBUG, "'%s' support low power encoding with %s", \
  183. device_path, vaProfileStr(profile)); \
  184. ret |= true; \
  185. }
  186. bool vaapi_display_h264_supported(VADisplay dpy, const char *device_path)
  187. {
  188. bool ret = false;
  189. CHECK_PROFILE(ret, VAProfileH264ConstrainedBaseline, dpy, device_path);
  190. CHECK_PROFILE(ret, VAProfileH264Main, dpy, device_path);
  191. CHECK_PROFILE(ret, VAProfileH264High, dpy, device_path);
  192. if (!ret) {
  193. CHECK_PROFILE_LP(ret, VAProfileH264ConstrainedBaseline, dpy,
  194. device_path);
  195. CHECK_PROFILE_LP(ret, VAProfileH264Main, dpy, device_path);
  196. CHECK_PROFILE_LP(ret, VAProfileH264High, dpy, device_path);
  197. }
  198. return ret;
  199. }
  200. bool vaapi_device_h264_supported(const char *device_path)
  201. {
  202. bool ret = false;
  203. VADisplay va_dpy;
  204. int drm_fd = -1;
  205. va_dpy = vaapi_open_device(&drm_fd, device_path,
  206. "vaapi_device_h264_supported");
  207. if (!va_dpy)
  208. return false;
  209. ret = vaapi_display_h264_supported(va_dpy, device_path);
  210. vaapi_close_device(&drm_fd, va_dpy);
  211. return ret;
  212. }
  213. const char *vaapi_get_h264_default_device()
  214. {
  215. static const char *default_h264_device = NULL;
  216. if (!default_h264_device) {
  217. bool ret = false;
  218. char path[32] = "/dev/dri/renderD1";
  219. for (int i = 28;; i++) {
  220. sprintf(path, "/dev/dri/renderD1%d", i);
  221. if (access(path, F_OK) != 0)
  222. break;
  223. ret = vaapi_device_h264_supported(path);
  224. if (ret) {
  225. default_h264_device = strdup(path);
  226. break;
  227. }
  228. }
  229. }
  230. return default_h264_device;
  231. }
  232. bool vaapi_display_av1_supported(VADisplay dpy, const char *device_path)
  233. {
  234. bool ret = false;
  235. CHECK_PROFILE(ret, VAProfileAV1Profile0, dpy, device_path);
  236. if (!ret) {
  237. CHECK_PROFILE_LP(ret, VAProfileAV1Profile0, dpy, device_path);
  238. }
  239. return ret;
  240. }
  241. bool vaapi_device_av1_supported(const char *device_path)
  242. {
  243. bool ret = false;
  244. VADisplay va_dpy;
  245. int drm_fd = -1;
  246. va_dpy = vaapi_open_device(&drm_fd, device_path,
  247. "vaapi_device_av1_supported");
  248. if (!va_dpy)
  249. return false;
  250. ret = vaapi_display_av1_supported(va_dpy, device_path);
  251. vaapi_close_device(&drm_fd, va_dpy);
  252. return ret;
  253. }
  254. const char *vaapi_get_av1_default_device()
  255. {
  256. static const char *default_av1_device = NULL;
  257. if (!default_av1_device) {
  258. bool ret = false;
  259. char path[32] = "/dev/dri/renderD1";
  260. for (int i = 28;; i++) {
  261. sprintf(path, "/dev/dri/renderD1%d", i);
  262. if (access(path, F_OK) != 0)
  263. break;
  264. ret = vaapi_device_av1_supported(path);
  265. if (ret) {
  266. default_av1_device = strdup(path);
  267. break;
  268. }
  269. }
  270. }
  271. return default_av1_device;
  272. }
  273. #ifdef ENABLE_HEVC
  274. bool vaapi_display_hevc_supported(VADisplay dpy, const char *device_path)
  275. {
  276. bool ret = false;
  277. CHECK_PROFILE(ret, VAProfileHEVCMain, dpy, device_path);
  278. CHECK_PROFILE(ret, VAProfileHEVCMain10, dpy, device_path);
  279. if (!ret) {
  280. CHECK_PROFILE_LP(ret, VAProfileHEVCMain, dpy, device_path);
  281. CHECK_PROFILE_LP(ret, VAProfileHEVCMain10, dpy, device_path);
  282. }
  283. return ret;
  284. }
  285. bool vaapi_device_hevc_supported(const char *device_path)
  286. {
  287. bool ret = false;
  288. VADisplay va_dpy;
  289. int drm_fd = -1;
  290. va_dpy = vaapi_open_device(&drm_fd, device_path,
  291. "vaapi_device_hevc_supported");
  292. if (!va_dpy)
  293. return false;
  294. ret = vaapi_display_hevc_supported(va_dpy, device_path);
  295. vaapi_close_device(&drm_fd, va_dpy);
  296. return ret;
  297. }
  298. const char *vaapi_get_hevc_default_device()
  299. {
  300. static const char *default_hevc_device = NULL;
  301. if (!default_hevc_device) {
  302. bool ret = false;
  303. char path[32] = "/dev/dri/renderD1";
  304. for (int i = 28;; i++) {
  305. sprintf(path, "/dev/dri/renderD1%d", i);
  306. if (access(path, F_OK) != 0)
  307. break;
  308. ret = vaapi_device_hevc_supported(path);
  309. if (ret) {
  310. default_hevc_device = strdup(path);
  311. break;
  312. }
  313. }
  314. }
  315. return default_hevc_device;
  316. }
  317. #endif // #ifdef ENABLE_HEVC