mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-11 19:06:33 +00:00
372 lines
12 KiB
C
372 lines
12 KiB
C
/* GStreamer
|
|
* Copyright (C) 2020 Igalia, S.L.
|
|
* Author: Víctor Jáquez <vjaquez@igalia.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
/**
|
|
* plugin-va:
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstvaav1dec.h"
|
|
#include "gstvacaps.h"
|
|
#include "gstvacompositor.h"
|
|
#include "gstvadeinterlace.h"
|
|
#include "gstvadevice.h"
|
|
#include "gstvafilter.h"
|
|
#include "gstvah264dec.h"
|
|
#include "gstvah264enc.h"
|
|
#include "gstvah265dec.h"
|
|
#include "gstvah265enc.h"
|
|
#include "gstvajpegdec.h"
|
|
#include "gstvampeg2dec.h"
|
|
#include "gstvaprofile.h"
|
|
#include "gstvavp8dec.h"
|
|
#include "gstvavp9dec.h"
|
|
#include "gstvavpp.h"
|
|
|
|
#define GST_CAT_DEFAULT gstva_debug
|
|
GST_DEBUG_CATEGORY (gstva_debug);
|
|
|
|
/* big bad mutex to exclusive access to shared stream buffers, such as
|
|
* DMABuf after a tee */
|
|
GRecMutex GST_VA_SHARED_LOCK = { 0, };
|
|
|
|
static void
|
|
plugin_add_dependencies (GstPlugin * plugin)
|
|
{
|
|
const gchar *env_vars[] = { "LIBVA_DRIVER_NAME", NULL };
|
|
const gchar *kernel_paths[] = { "/dev/dri", NULL };
|
|
const gchar *kernel_names[] = { "renderD", NULL };
|
|
|
|
/* features get updated upon changes in /dev/dri/renderD* */
|
|
gst_plugin_add_dependency (plugin, NULL, kernel_paths, kernel_names,
|
|
GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_PREFIX);
|
|
|
|
/* features get updated upon changes on LIBVA_DRIVER_NAME envvar */
|
|
gst_plugin_add_dependency (plugin, env_vars, NULL, NULL,
|
|
GST_PLUGIN_DEPENDENCY_FLAG_NONE);
|
|
|
|
/* features get updated upon changes in default VA drivers
|
|
* directory */
|
|
gst_plugin_add_dependency_simple (plugin, "LIBVA_DRIVERS_PATH",
|
|
LIBVA_DRIVERS_PATH, "_drv_video.so",
|
|
GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_SUFFIX |
|
|
GST_PLUGIN_DEPENDENCY_FLAG_PATHS_ARE_DEFAULT_ONLY);
|
|
}
|
|
|
|
static void
|
|
plugin_register_decoders (GstPlugin * plugin, GstVaDevice * device,
|
|
GHashTable * decoders)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_hash_table_iter_init (&iter, decoders);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
guint32 codec = *((gint64 *) key);
|
|
GArray *profiles = (GArray *) value;
|
|
GstCaps *sinkcaps = NULL, *srccaps = NULL;
|
|
|
|
if (!profiles || profiles->len == 0)
|
|
continue;
|
|
|
|
if (!gst_va_caps_from_profiles (device->display, profiles, VAEntrypointVLD,
|
|
&sinkcaps, &srccaps))
|
|
continue;
|
|
|
|
GST_LOG ("%d decoder codec: %" GST_FOURCC_FORMAT, profiles->len,
|
|
GST_FOURCC_ARGS (codec));
|
|
GST_LOG ("sink caps: %" GST_PTR_FORMAT, sinkcaps);
|
|
GST_LOG ("src caps: %" GST_PTR_FORMAT, srccaps);
|
|
|
|
switch (codec) {
|
|
case H264:
|
|
if (!gst_va_h264_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register H264 decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
case HEVC:
|
|
if (!gst_va_h265_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register H265 decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
case VP8:
|
|
if (!gst_va_vp8_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register VP8 decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
case VP9:
|
|
if (!gst_va_vp9_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register VP9 decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
case MPEG2:
|
|
if (!gst_va_mpeg2_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register Mpeg2 decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
#if VA_CHECK_VERSION(1, 8, 0)
|
|
case AV1:
|
|
if (!gst_va_av1_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register AV1 decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
#endif
|
|
case JPEG:
|
|
if (!gst_va_jpeg_dec_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register JPEG decoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
default:
|
|
GST_DEBUG ("No decoder implementation for %" GST_FOURCC_FORMAT,
|
|
GST_FOURCC_ARGS (codec));
|
|
break;
|
|
}
|
|
|
|
gst_caps_unref (srccaps);
|
|
gst_caps_unref (sinkcaps);
|
|
}
|
|
}
|
|
|
|
static void
|
|
plugin_register_encoders (GstPlugin * plugin, GstVaDevice * device,
|
|
GHashTable * encoders, VAEntrypoint entrypoint)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_hash_table_iter_init (&iter, encoders);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
guint32 codec = *((gint64 *) key);
|
|
GArray *profiles = (GArray *) value;
|
|
GstCaps *sinkcaps = NULL, *srccaps = NULL;
|
|
|
|
if (!profiles || profiles->len == 0)
|
|
continue;
|
|
|
|
if (!gst_va_caps_from_profiles (device->display, profiles, entrypoint,
|
|
&srccaps, &sinkcaps))
|
|
continue;
|
|
|
|
GST_LOG ("%d encoder %scodec: %" GST_FOURCC_FORMAT, profiles->len,
|
|
(entrypoint == VAEntrypointEncSliceLP) ? "low power " : "",
|
|
GST_FOURCC_ARGS (codec));
|
|
GST_LOG ("sink caps: %" GST_PTR_FORMAT, sinkcaps);
|
|
GST_LOG ("src caps: %" GST_PTR_FORMAT, srccaps);
|
|
|
|
switch (codec) {
|
|
case H264:
|
|
if (!gst_va_h264_enc_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE, entrypoint)) {
|
|
GST_WARNING ("Failed to register H264 encoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
case HEVC:
|
|
if (!gst_va_h265_enc_register (plugin, device, sinkcaps, srccaps,
|
|
GST_RANK_NONE, entrypoint)) {
|
|
GST_WARNING ("Failed to register H265 encoder: %s",
|
|
device->render_device_path);
|
|
}
|
|
break;
|
|
default:
|
|
GST_DEBUG ("No encoder implementation for %" GST_FOURCC_FORMAT,
|
|
GST_FOURCC_ARGS (codec));
|
|
break;
|
|
}
|
|
|
|
gst_caps_unref (srccaps);
|
|
gst_caps_unref (sinkcaps);
|
|
}
|
|
}
|
|
|
|
static void
|
|
plugin_register_vpp (GstPlugin * plugin, GstVaDevice * device)
|
|
{
|
|
GstVaFilter *filter;
|
|
gboolean has_colorbalance, has_deinterlace, has_compose;
|
|
|
|
has_colorbalance = FALSE;
|
|
has_deinterlace = FALSE;
|
|
has_compose = FALSE;
|
|
filter = gst_va_filter_new (device->display);
|
|
if (gst_va_filter_open (filter)) {
|
|
has_colorbalance =
|
|
gst_va_filter_has_filter (filter, VAProcFilterColorBalance);
|
|
has_deinterlace =
|
|
gst_va_filter_has_filter (filter, VAProcFilterDeinterlacing);
|
|
has_compose = gst_va_filter_has_compose (filter);
|
|
} else {
|
|
GST_WARNING ("Failed open VA filter");
|
|
gst_object_unref (filter);
|
|
return;
|
|
}
|
|
gst_object_unref (filter);
|
|
|
|
if (!gst_va_vpp_register (plugin, device, has_colorbalance, GST_RANK_NONE))
|
|
GST_WARNING ("Failed to register postproc: %s", device->render_device_path);
|
|
|
|
if (has_deinterlace) {
|
|
if (!gst_va_deinterlace_register (plugin, device, GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register deinterlace: %s",
|
|
device->render_device_path);
|
|
}
|
|
}
|
|
|
|
if (has_compose) {
|
|
if (!gst_va_compositor_register (plugin, device, GST_RANK_NONE)) {
|
|
GST_WARNING ("Failed to register compositor: %s",
|
|
device->render_device_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
_insert_profile_in_table (GHashTable * table, VAProfile profile)
|
|
{
|
|
gint64 codec = gst_va_profile_codec (profile);
|
|
GArray *profiles;
|
|
|
|
if (codec == GST_MAKE_FOURCC ('N', 'O', 'N', 'E'))
|
|
return;
|
|
|
|
profiles = g_hash_table_lookup (table, &codec);
|
|
if (!profiles) {
|
|
gint64 *codec_ptr = g_new (gint64, 1);
|
|
|
|
*codec_ptr = codec;
|
|
profiles = g_array_new (FALSE, FALSE, sizeof (VAProfile));
|
|
g_hash_table_insert (table, codec_ptr, profiles);
|
|
}
|
|
g_array_append_val (profiles, profile);
|
|
}
|
|
|
|
static gboolean
|
|
plugin_register_elements (GstPlugin * plugin, GstVaDevice * device)
|
|
{
|
|
VADisplay dpy = gst_va_display_get_va_dpy (device->display);
|
|
VAEntrypoint *entrypoints = g_new (VAEntrypoint, vaMaxNumEntrypoints (dpy));
|
|
VAProfile *profiles = g_new (VAProfile, vaMaxNumProfiles (dpy));
|
|
VAStatus status;
|
|
GHashTable *decoders, *encoders, *encoderslp, *encodersimg;
|
|
gint i, j, num_entrypoints = 0, num_profiles = 0;
|
|
gboolean has_vpp = FALSE, ret = FALSE;
|
|
|
|
decoders = g_hash_table_new_full (g_int64_hash, g_int64_equal,
|
|
(GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
|
|
encoders = g_hash_table_new_full (g_int64_hash, g_int64_equal,
|
|
(GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
|
|
encoderslp = g_hash_table_new_full (g_int64_hash, g_int64_equal,
|
|
(GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
|
|
encodersimg = g_hash_table_new_full (g_int64_hash, g_int64_equal,
|
|
(GDestroyNotify) g_free, (GDestroyNotify) g_array_unref);
|
|
|
|
status = vaQueryConfigProfiles (dpy, profiles, &num_profiles);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR ("vaQueryConfigProfile: %s", vaErrorStr (status));
|
|
goto bail;
|
|
}
|
|
|
|
for (i = 0; i < num_profiles; i++) {
|
|
status = vaQueryConfigEntrypoints (dpy, profiles[i], entrypoints,
|
|
&num_entrypoints);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR ("vaQueryConfigEntrypoints: %s", vaErrorStr (status));
|
|
goto bail;
|
|
}
|
|
|
|
for (j = 0; j < num_entrypoints; j++) {
|
|
if (entrypoints[j] == VAEntrypointVLD)
|
|
_insert_profile_in_table (decoders, profiles[i]);
|
|
else if (entrypoints[j] == VAEntrypointEncSlice)
|
|
_insert_profile_in_table (encoders, profiles[i]);
|
|
else if (entrypoints[j] == VAEntrypointEncSliceLP)
|
|
_insert_profile_in_table (encoderslp, profiles[i]);
|
|
else if (entrypoints[j] == VAEntrypointEncPicture)
|
|
_insert_profile_in_table (encodersimg, profiles[i]);
|
|
else if (entrypoints[j] == VAEntrypointVideoProc)
|
|
has_vpp = TRUE;
|
|
}
|
|
}
|
|
|
|
plugin_register_decoders (plugin, device, decoders);
|
|
plugin_register_encoders (plugin, device, encoders, VAEntrypointEncSlice);
|
|
plugin_register_encoders (plugin, device, encoderslp, VAEntrypointEncSliceLP);
|
|
plugin_register_encoders (plugin, device, encodersimg,
|
|
VAEntrypointEncPicture);
|
|
if (has_vpp)
|
|
plugin_register_vpp (plugin, device);
|
|
|
|
ret = TRUE;
|
|
|
|
bail:
|
|
g_hash_table_unref (encodersimg);
|
|
g_hash_table_unref (encoderslp);
|
|
g_hash_table_unref (encoders);
|
|
g_hash_table_unref (decoders);
|
|
g_free (entrypoints);
|
|
g_free (profiles);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
plugin_init (GstPlugin * plugin)
|
|
{
|
|
GList *devices, *dev;
|
|
gboolean ret = TRUE;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gstva_debug, "va", 0, "VA general debug");
|
|
|
|
plugin_add_dependencies (plugin);
|
|
|
|
devices = gst_va_device_find_devices ();
|
|
for (dev = devices; dev; dev = g_list_next (dev)) {
|
|
if (!plugin_register_elements (plugin, dev->data)) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
gst_va_device_list_free (devices);
|
|
|
|
return ret;
|
|
}
|
|
|
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
GST_VERSION_MINOR,
|
|
va, "VA-API codecs plugin",
|
|
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|