File indexing completed on 2024-05-05 05:30:19
0001 /* 0002 SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "vaapiutils_p.h" 0008 #include <logging_record.h> 0009 0010 #include <QDir> 0011 0012 extern "C" { 0013 #include <fcntl.h> 0014 #include <unistd.h> 0015 #include <va/va_drm.h> 0016 #include <xf86drm.h> 0017 } 0018 0019 VaapiUtils::VaapiUtils() 0020 { 0021 int max_devices = drmGetDevices2(0, nullptr, 0); 0022 if (max_devices <= 0) { 0023 qCWarning(PIPEWIRERECORD_LOGGING) << "drmGetDevices2() has not found any devices (errno=" << -max_devices << ")"; 0024 return; 0025 } 0026 0027 std::vector<drmDevicePtr> devices(max_devices); 0028 int ret = drmGetDevices2(0, devices.data(), max_devices); 0029 if (ret < 0) { 0030 qCWarning(PIPEWIRERECORD_LOGGING) << "drmGetDevices2() returned an error " << ret; 0031 return; 0032 } 0033 0034 for (const drmDevicePtr &device : devices) { 0035 if (device->available_nodes & (1 << DRM_NODE_RENDER)) { 0036 QByteArray fullPath = device->nodes[DRM_NODE_RENDER]; 0037 if (supportsH264(fullPath)) { 0038 m_devicePath = fullPath; 0039 break; 0040 } 0041 break; 0042 } 0043 } 0044 0045 drmFreeDevices(devices.data(), ret); 0046 0047 if (m_devicePath.isEmpty()) { 0048 qCWarning(PIPEWIRERECORD_LOGGING) << "DRM device not found"; 0049 } 0050 } 0051 0052 VaapiUtils::~VaapiUtils() 0053 { 0054 } 0055 0056 bool VaapiUtils::supportsProfile(VAProfile profile) 0057 { 0058 if (m_devicePath.isEmpty()) { 0059 return false; 0060 } 0061 bool ret = false; 0062 0063 int drmFd = -1; 0064 0065 VADisplay vaDpy = openDevice(&drmFd, m_devicePath); 0066 if (!vaDpy) { 0067 return false; 0068 } 0069 0070 ret = supportsProfile(profile, vaDpy, m_devicePath); 0071 0072 closeDevice(&drmFd, vaDpy); 0073 0074 return ret; 0075 } 0076 0077 bool VaapiUtils::supportsH264(const QByteArray &path) const 0078 { 0079 if (path.isEmpty()) { 0080 return false; 0081 } 0082 bool ret = false; 0083 0084 int drmFd = -1; 0085 0086 VADisplay vaDpy = openDevice(&drmFd, path); 0087 if (!vaDpy) { 0088 return false; 0089 } 0090 0091 ret = supportsProfile(VAProfileH264ConstrainedBaseline, vaDpy, path) || supportsProfile(VAProfileH264Main, vaDpy, path) 0092 || supportsProfile(VAProfileH264High, vaDpy, path); 0093 0094 querySizeConstraints(vaDpy); 0095 0096 /** 0097 * If FFPEG fails to import a buffer with modifiers, it silently goes into 0098 * of importing as linear, which looks to us like it works, but obviously results in 0099 * a messed up image. At the time of writing the Intel iHD driver does not 0100 * 0101 * Manually blacklist drivers which are known to fail import 0102 * 0103 * 10/7/23 - FFmpeg 2.6 with intel-media-driver 23.2.3-1 0104 */ 0105 const bool blackListed = QByteArray(vaQueryVendorString(vaDpy)).startsWith("Intel iHD driver"); 0106 0107 const bool disabledByEnvVar = qEnvironmentVariableIntValue("KPIPEWIRE_NO_MODIFIERS_FOR_ENCODING") > 0; 0108 0109 if (blackListed || disabledByEnvVar) { 0110 m_supportsHardwareModifiers = false; 0111 } 0112 0113 closeDevice(&drmFd, vaDpy); 0114 0115 return ret; 0116 } 0117 0118 QByteArray VaapiUtils::devicePath() 0119 { 0120 return m_devicePath; 0121 } 0122 0123 QSize VaapiUtils::minimumSize() const 0124 { 0125 return m_minSize; 0126 } 0127 0128 QSize VaapiUtils::maximumSize() const 0129 { 0130 return m_maxSize; 0131 } 0132 0133 bool VaapiUtils::supportsHardwareModifiers() const 0134 { 0135 return m_supportsHardwareModifiers; 0136 } 0137 0138 VADisplay VaapiUtils::openDevice(int *fd, const QByteArray &path) 0139 { 0140 VADisplay vaDpy; 0141 0142 if (path.isEmpty()) { 0143 return NULL; 0144 } 0145 0146 *fd = open(path.data(), O_RDWR); 0147 if (*fd < 0) { 0148 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: Failed to open device" << path; 0149 return NULL; 0150 } 0151 0152 vaDpy = vaGetDisplayDRM(*fd); 0153 if (!vaDpy) { 0154 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: Failed to initialize DRM display"; 0155 return NULL; 0156 } 0157 0158 if (vaDisplayIsValid(vaDpy) == 0) { 0159 qCWarning(PIPEWIRERECORD_LOGGING) << "Invalid VA display"; 0160 vaTerminate(vaDpy); 0161 return NULL; 0162 } 0163 0164 int major, minor; 0165 VAStatus va_status = vaInitialize(vaDpy, &major, &minor); 0166 0167 if (va_status != VA_STATUS_SUCCESS) { 0168 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: Failed to initialize display"; 0169 return NULL; 0170 } 0171 0172 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: Display initialized"; 0173 0174 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: API version" << major << "." << minor; 0175 0176 const char *driver = vaQueryVendorString(vaDpy); 0177 0178 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI:" << driver << "in use for device" << path; 0179 0180 return vaDpy; 0181 } 0182 0183 void VaapiUtils::closeDevice(int *fd, VADisplay dpy) 0184 { 0185 vaTerminate(dpy); 0186 if (*fd < 0) { 0187 return; 0188 } 0189 0190 close(*fd); 0191 *fd = -1; 0192 } 0193 0194 bool VaapiUtils::supportsProfile(VAProfile profile, VADisplay dpy, const QByteArray &path) 0195 { 0196 uint32_t ret = rateControlForProfile(profile, VAEntrypointEncSlice, dpy, path); 0197 0198 if (ret & VA_RC_CBR || ret & VA_RC_CQP || ret & VA_RC_VBR) { 0199 return true; 0200 } else { 0201 ret = rateControlForProfile(profile, VAEntrypointEncSliceLP, dpy, path); 0202 0203 if (ret & VA_RC_CBR || ret & VA_RC_CQP || ret & VA_RC_VBR) { 0204 return true; 0205 } 0206 } 0207 0208 return false; 0209 } 0210 0211 uint32_t VaapiUtils::rateControlForProfile(VAProfile profile, VAEntrypoint entrypoint, VADisplay dpy, const QByteArray &path) 0212 { 0213 VAStatus va_status; 0214 VAConfigAttrib attrib[1]; 0215 attrib->type = VAConfigAttribRateControl; 0216 0217 va_status = vaGetConfigAttributes(dpy, profile, entrypoint, attrib, 1); 0218 0219 switch (va_status) { 0220 case VA_STATUS_SUCCESS: 0221 return attrib->value; 0222 case VA_STATUS_ERROR_UNSUPPORTED_PROFILE: 0223 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: profile" << profile << "is not supported by the device" << path; 0224 return 0; 0225 case VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT: 0226 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: entrypoint" << entrypoint << "of profile" << profile << "is not supported by the device" << path; 0227 return 0; 0228 default: 0229 qCWarning(PIPEWIRERECORD_LOGGING) << "VAAPI: Fail to get RC attribute from the" << profile << entrypoint << "of the device" << path; 0230 return 0; 0231 } 0232 } 0233 0234 void VaapiUtils::querySizeConstraints(VADisplay dpy) const 0235 { 0236 VAConfigID config; 0237 if (auto status = vaCreateConfig(dpy, VAProfileH264ConstrainedBaseline, VAEntrypointEncSlice, nullptr, 0, &config); status != VA_STATUS_SUCCESS) { 0238 return; 0239 } 0240 0241 VASurfaceAttrib attrib[8]; 0242 uint32_t attribCount = 8; 0243 0244 auto status = vaQuerySurfaceAttributes(dpy, config, attrib, &attribCount); 0245 if (status == VA_STATUS_SUCCESS) { 0246 for (uint32_t i = 0; i < attribCount; ++i) { 0247 switch (attrib[i].type) { 0248 case VASurfaceAttribMinWidth: 0249 m_minSize.setWidth(attrib[i].value.value.i); 0250 break; 0251 case VASurfaceAttribMinHeight: 0252 m_minSize.setHeight(attrib[i].value.value.i); 0253 break; 0254 case VASurfaceAttribMaxWidth: 0255 m_maxSize.setWidth(attrib[i].value.value.i); 0256 break; 0257 case VASurfaceAttribMaxHeight: 0258 m_maxSize.setHeight(attrib[i].value.value.i); 0259 break; 0260 default: 0261 break; 0262 } 0263 } 0264 } 0265 0266 vaDestroyConfig(dpy, config); 0267 }