This commit is contained in:
2026-02-13 13:06:50 +09:00
commit b54066842b
249 changed files with 69547 additions and 0 deletions

323
grd-hwaccel-vaapi.c Normal file
View File

@@ -0,0 +1,323 @@
/*
* Copyright (C) 2022 Pascal Nowack
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include "grd-hwaccel-vaapi.h"
#include <inttypes.h>
#include <fcntl.h>
#include <gio/gio.h>
#include <glib/gstdio.h>
#include <va/va.h>
#include <va/va_drm.h>
#include "grd-encode-session-vaapi.h"
#include "grd-vk-device.h"
struct _GrdHwAccelVaapi
{
GObject parent;
GrdVkDevice *vk_device;
int drm_fd;
VADisplay va_display;
gboolean supports_quality_level;
};
G_DEFINE_TYPE (GrdHwAccelVaapi, grd_hwaccel_vaapi, G_TYPE_OBJECT)
GrdEncodeSession *
grd_hwaccel_vaapi_create_encode_session (GrdHwAccelVaapi *hwaccel_vaapi,
uint32_t source_width,
uint32_t source_height,
uint32_t refresh_rate,
GError **error)
{
return (GrdEncodeSession *) grd_encode_session_vaapi_new (hwaccel_vaapi->vk_device,
hwaccel_vaapi->va_display,
hwaccel_vaapi->supports_quality_level,
source_width, source_height,
refresh_rate, error);
}
static gboolean
initialize_vaapi (GrdHwAccelVaapi *hwaccel_vaapi,
int *version_major,
int *version_minor,
int64_t render_minor,
GError **error)
{
g_autofree char *render_node = NULL;
VAStatus va_status;
render_node = g_strdup_printf ("/dev/dri/renderD%" PRIi64, render_minor);
hwaccel_vaapi->drm_fd = g_open (render_node, O_RDWR | O_CLOEXEC, 600);
if (hwaccel_vaapi->drm_fd == -1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to open render node: %s", g_strerror (errno));
return FALSE;
}
hwaccel_vaapi->va_display = vaGetDisplayDRM (hwaccel_vaapi->drm_fd);
if (!hwaccel_vaapi->va_display)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to get VA display from DRM fd");
return FALSE;
}
va_status = vaInitialize (hwaccel_vaapi->va_display,
version_major, version_minor);
if (va_status != VA_STATUS_SUCCESS)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to initialize VA display: %s", vaErrorStr (va_status));
return FALSE;
}
return TRUE;
}
static gboolean
has_device_avc_profile (VAProfile *profiles,
int n_profiles)
{
int i;
for (i = 0; i < n_profiles; ++i)
{
if (profiles[i] == VAProfileH264High)
return TRUE;
}
return FALSE;
}
static gboolean
has_device_avc_enc_entrypoint (VAEntrypoint *entrypoints,
int n_entrypoints)
{
int i;
for (i = 0; i < n_entrypoints; ++i)
{
if (entrypoints[i] == VAEntrypointEncSlice)
return TRUE;
}
return FALSE;
}
static gboolean
check_device_capabilities (GrdHwAccelVaapi *hwaccel_vaapi,
GError **error)
{
g_autofree VAProfile *profiles = NULL;
g_autofree VAEntrypoint *entrypoints = NULL;
VAConfigAttrib attributes[] =
{
{ .type = VAConfigAttribRTFormat },
{ .type = VAConfigAttribRateControl },
{ .type = VAConfigAttribEncMaxRefFrames },
{ .type = VAConfigAttribEncPackedHeaders },
{ .type = VAConfigAttribEncQualityRange },
};
int max_profiles;
int max_num_entrypoints;
int n_profiles = 0;
int n_entrypoints = 0;
uint32_t max_ref_l0;
VAStatus va_status;
max_profiles = vaMaxNumProfiles (hwaccel_vaapi->va_display);
if (max_profiles < 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid max num profiles: %i", max_profiles);
return FALSE;
}
profiles = g_new0 (VAProfile, max_profiles);
va_status = vaQueryConfigProfiles (hwaccel_vaapi->va_display,
profiles, &n_profiles);
if (va_status != VA_STATUS_SUCCESS)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to query config profiles: %s",
vaErrorStr (va_status));
return FALSE;
}
if (!has_device_avc_profile (profiles, n_profiles))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Unsuitable device, missing required AVC profile");
return FALSE;
}
max_num_entrypoints = vaMaxNumEntrypoints (hwaccel_vaapi->va_display);
if (max_num_entrypoints < 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid max num entrypoints: %i", max_num_entrypoints);
return FALSE;
}
entrypoints = g_new0 (VAEntrypoint, max_num_entrypoints);
va_status = vaQueryConfigEntrypoints (hwaccel_vaapi->va_display,
VAProfileH264High,
entrypoints, &n_entrypoints);
if (va_status != VA_STATUS_SUCCESS)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to query config entrypoints: %s",
vaErrorStr (va_status));
return FALSE;
}
if (!has_device_avc_enc_entrypoint (entrypoints, n_entrypoints))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Unsuitable device, missing required AVC encoding "
"entrypoint");
return FALSE;
}
va_status = vaGetConfigAttributes (hwaccel_vaapi->va_display,
VAProfileH264High, VAEntrypointEncSlice,
attributes, G_N_ELEMENTS (attributes));
if (va_status != VA_STATUS_SUCCESS)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to get config attributes: %s",
vaErrorStr (va_status));
return FALSE;
}
g_assert (attributes[0].type == VAConfigAttribRTFormat);
if (attributes[0].value == VA_ATTRIB_NOT_SUPPORTED ||
!(attributes[0].value & VA_RT_FORMAT_YUV420))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Unsuitable device, device does not support YUV420 format");
return FALSE;
}
g_assert (attributes[1].type == VAConfigAttribRateControl);
if (attributes[1].value == VA_ATTRIB_NOT_SUPPORTED ||
!(attributes[1].value & VA_RC_CQP))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Unsuitable device, device does not support CQP");
return FALSE;
}
g_assert (attributes[2].type == VAConfigAttribEncMaxRefFrames);
if (attributes[2].value == VA_ATTRIB_NOT_SUPPORTED)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Unsuitable device, invalid max ref frames attribute: %u",
attributes[2].value);
return FALSE;
}
max_ref_l0 = attributes[2].value & 0xFFFF;
if (max_ref_l0 < 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Unsuitable device, device cannot handle reference frames");
return FALSE;
}
g_assert (attributes[3].type == VAConfigAttribEncPackedHeaders);
if (attributes[3].value == VA_ATTRIB_NOT_SUPPORTED ||
!(attributes[3].value & VA_ENC_PACKED_HEADER_SEQUENCE) ||
!(attributes[3].value & VA_ENC_PACKED_HEADER_PICTURE) ||
!(attributes[3].value & VA_ENC_PACKED_HEADER_SLICE) ||
!(attributes[3].value & VA_ENC_PACKED_HEADER_RAW_DATA))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Unsuitable device, device does not support required packed "
"headers (only supports 0x%08X)", attributes[3].value);
return FALSE;
}
g_assert (attributes[4].type == VAConfigAttribEncQualityRange);
hwaccel_vaapi->supports_quality_level = attributes[4].value !=
VA_ATTRIB_NOT_SUPPORTED;
return TRUE;
}
GrdHwAccelVaapi *
grd_hwaccel_vaapi_new (GrdVkDevice *vk_device,
GError **error)
{
g_autoptr (GrdHwAccelVaapi) hwaccel_vaapi = NULL;
int version_major = 0;
int version_minor = 0;
g_assert (vk_device);
hwaccel_vaapi = g_object_new (GRD_TYPE_HWACCEL_VAAPI, NULL);
hwaccel_vaapi->vk_device = vk_device;
if (!initialize_vaapi (hwaccel_vaapi, &version_major, &version_minor,
grd_vk_device_get_drm_render_node (vk_device),
error))
return NULL;
if (!check_device_capabilities (hwaccel_vaapi, error))
return NULL;
g_message ("[HWAccel.VAAPI] Successfully initialized VAAPI %i.%i with "
"vendor: %s", version_major, version_minor,
vaQueryVendorString (hwaccel_vaapi->va_display));
return g_steal_pointer (&hwaccel_vaapi);
}
static void
grd_hwaccel_vaapi_dispose (GObject *object)
{
GrdHwAccelVaapi *hwaccel_vaapi = GRD_HWACCEL_VAAPI (object);
g_clear_pointer (&hwaccel_vaapi->va_display, vaTerminate);
g_clear_fd (&hwaccel_vaapi->drm_fd, NULL);
G_OBJECT_CLASS (grd_hwaccel_vaapi_parent_class)->dispose (object);
}
static void
grd_hwaccel_vaapi_init (GrdHwAccelVaapi *hwaccel_vaapi)
{
}
static void
grd_hwaccel_vaapi_class_init (GrdHwAccelVaapiClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = grd_hwaccel_vaapi_dispose;
}