vaapi-utils.c 9.0 KB

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