mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 21:51:09 +00:00
ed9cc7c8d2
Add --deinterlace option to enable deinterlacing through explicit VA/VPP deinterlacing filter. However, if --deinterlace option is not set but the --deinterlace-flags option is set with "top-field-first", then the very basic bob deinterlacing filter is set through VA/VPP proc pipeline flags.
446 lines
13 KiB
C
Executable file
446 lines
13 KiB
C
Executable file
/*
|
|
* test-filter.c - Test GstVaapiFilter
|
|
*
|
|
* Copyright (C) 2010-2011 Splitted-Desktop Systems
|
|
* Copyright (C) 2012-2013 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "gst/vaapi/sysdeps.h"
|
|
#include <errno.h>
|
|
#include <gst/vaapi/gstvaapifilter.h>
|
|
#include <gst/vaapi/gstvaapiwindow.h>
|
|
#include "image.h"
|
|
#include "output.h"
|
|
|
|
static gchar *g_src_format_str;
|
|
static gchar *g_crop_rect_str;
|
|
static gchar *g_denoise_str;
|
|
static gchar *g_sharpen_str;
|
|
static gchar *g_deinterlace_str;
|
|
static gchar *g_deinterlace_flags_str;
|
|
|
|
static GOptionEntry g_options[] = {
|
|
{ "src-format", 's',
|
|
0,
|
|
G_OPTION_ARG_STRING, &g_src_format_str,
|
|
"source surface format", NULL },
|
|
{ "crop-rect", 'c',
|
|
0,
|
|
G_OPTION_ARG_STRING, &g_crop_rect_str,
|
|
"cropping rectangle", NULL },
|
|
{ "denoise", 0,
|
|
0,
|
|
G_OPTION_ARG_STRING, &g_denoise_str,
|
|
"set noise reduction level", NULL },
|
|
{ "sharpen", 0,
|
|
0,
|
|
G_OPTION_ARG_STRING, &g_sharpen_str,
|
|
"set sharpening level", NULL },
|
|
{ "deinterlace", 0,
|
|
0,
|
|
G_OPTION_ARG_STRING, &g_deinterlace_str,
|
|
"enable deinterlacing", NULL },
|
|
{ "deinterlace-flags", 0,
|
|
0,
|
|
G_OPTION_ARG_STRING, &g_deinterlace_flags_str,
|
|
"deinterlacing flags", NULL },
|
|
{ NULL, }
|
|
};
|
|
|
|
#define APP_ERROR app_error_quark()
|
|
static GQuark
|
|
app_error_quark(void)
|
|
{
|
|
static gsize g_quark;
|
|
|
|
if (g_once_init_enter(&g_quark)) {
|
|
gsize quark = (gsize)g_quark_from_static_string("AppError");
|
|
g_once_init_leave(&g_quark, quark);
|
|
}
|
|
return g_quark;
|
|
}
|
|
|
|
typedef enum {
|
|
APP_ERROR_NONE,
|
|
APP_ERROR_CREATE_TEST_SURFACE,
|
|
} AppError;
|
|
|
|
static inline void
|
|
pause(void)
|
|
{
|
|
g_print("Press any key to continue...\n");
|
|
getchar();
|
|
}
|
|
|
|
static GstVaapiSurface *
|
|
create_test_surface(GstVaapiDisplay *display, guint width, guint height,
|
|
guint flags, GError **error_ptr)
|
|
{
|
|
GstVideoFormat format = GST_VIDEO_FORMAT_I420;
|
|
GstVaapiSurface *surface = NULL;
|
|
GstVaapiImage *image = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (g_src_format_str) {
|
|
format = gst_vaapi_video_format_from_string(g_src_format_str);
|
|
if (format == GST_VIDEO_FORMAT_UNKNOWN)
|
|
goto error_invalid_format;
|
|
}
|
|
|
|
surface = gst_vaapi_surface_new_with_format(display, format, width, height);
|
|
if (!surface)
|
|
goto error_create_surface;
|
|
|
|
image = image_generate_full(display, format, width, height, flags);
|
|
if (!image)
|
|
goto error_create_image;
|
|
|
|
if (!image_upload(image, surface))
|
|
goto error_upload_image;
|
|
|
|
gst_vaapi_object_unref(image);
|
|
return surface;
|
|
|
|
/* ERRORS */
|
|
error_invalid_format:
|
|
error = g_error_new(APP_ERROR, APP_ERROR_CREATE_TEST_SURFACE,
|
|
"unknown format %s", g_src_format_str);
|
|
goto error_cleanup;
|
|
error_create_surface:
|
|
error = g_error_new(APP_ERROR, APP_ERROR_CREATE_TEST_SURFACE,
|
|
"unsupported format %s", gst_vaapi_video_format_to_string(format));
|
|
goto error_cleanup;
|
|
error_create_image:
|
|
error = g_error_new(APP_ERROR, APP_ERROR_CREATE_TEST_SURFACE,
|
|
"unsupported %s image", gst_vaapi_video_format_to_string(format));
|
|
goto error_cleanup;
|
|
error_upload_image:
|
|
error = g_error_new(APP_ERROR, APP_ERROR_CREATE_TEST_SURFACE,
|
|
"failed to upload %s image", gst_vaapi_video_format_to_string(format));
|
|
goto error_cleanup;
|
|
error_cleanup:
|
|
if (image)
|
|
gst_vaapi_object_unref(image);
|
|
if (surface)
|
|
gst_vaapi_object_unref(surface);
|
|
if (error_ptr)
|
|
*error_ptr = error;
|
|
else
|
|
g_error_free(error);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
dump_operation(GstVaapiFilterOpInfo *op_info)
|
|
{
|
|
GParamSpec * const pspec = op_info->pspec;
|
|
GValue value = G_VALUE_INIT;
|
|
gchar *value_str;
|
|
|
|
if (!op_info)
|
|
return;
|
|
|
|
g_print(" %s: ", g_param_spec_get_name(pspec));
|
|
g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
|
|
g_param_value_set_default(pspec, &value);
|
|
value_str = g_strdup_value_contents(&value);
|
|
g_print("%s (default: %s)\n", G_VALUE_TYPE_NAME(&value),
|
|
value_str ? value_str : "<unknown>");
|
|
g_free(value_str);
|
|
}
|
|
|
|
static void
|
|
dump_operations(GstVaapiFilter *filter)
|
|
{
|
|
GPtrArray * const ops = gst_vaapi_filter_get_operations(filter);
|
|
guint i;
|
|
|
|
if (!ops)
|
|
return;
|
|
|
|
g_print("%u operations\n", ops->len);
|
|
for (i = 0; i < ops->len; i++)
|
|
dump_operation(g_ptr_array_index(ops, i));
|
|
g_ptr_array_unref(ops);
|
|
}
|
|
|
|
static void
|
|
dump_formats(GstVaapiFilter *filter)
|
|
{
|
|
GArray * const formats = gst_vaapi_filter_get_formats(filter);
|
|
guint i;
|
|
|
|
if (!formats)
|
|
return;
|
|
|
|
g_print("%u formats\n", formats->len);
|
|
for (i = 0; i < formats->len; i++) {
|
|
GstVideoFormat format = g_array_index(formats, GstVideoFormat, i);
|
|
g_print(" %s\n", gst_vaapi_video_format_to_string(format));
|
|
}
|
|
g_array_unref(formats);
|
|
}
|
|
|
|
static gboolean
|
|
parse_double(const gchar *str, gdouble *out_value_ptr)
|
|
{
|
|
gchar *endptr = NULL;
|
|
gdouble out_value;
|
|
|
|
g_return_val_if_fail(out_value_ptr != NULL, FALSE);
|
|
|
|
errno = 0;
|
|
out_value = g_ascii_strtod(str, &endptr);
|
|
if (!endptr || *endptr != '\0' || errno == ERANGE)
|
|
return FALSE;
|
|
|
|
*out_value_ptr = out_value;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_crop_rect(const gchar *str, GstVaapiRectangle *crop_rect)
|
|
{
|
|
if (str) {
|
|
// Format: <WIDTH> 'x' <HEIGHT>
|
|
if (sscanf(str, "%ux%u", &crop_rect->width, &crop_rect->height) == 2) {
|
|
crop_rect->x = 0;
|
|
crop_rect->y = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
// Format: '('? <X> ',' <Y> ')'? <WIDTH> 'x' <HEIGHT>
|
|
if (sscanf(str, "(%d,%d):%ux%u", &crop_rect->x, &crop_rect->y,
|
|
&crop_rect->width, &crop_rect->height) == 4 ||
|
|
sscanf(str, "%d,%d:%ux%u", &crop_rect->x, &crop_rect->y,
|
|
&crop_rect->width, &crop_rect->height) == 4)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_enum(const gchar *str, GType type, gint default_value,
|
|
gint *out_value_ptr)
|
|
{
|
|
gint out_value = default_value;
|
|
|
|
g_return_val_if_fail(out_value_ptr != NULL, FALSE);
|
|
|
|
if (str) {
|
|
GEnumClass * const enum_class = g_type_class_ref(type);
|
|
if (!enum_class)
|
|
return FALSE;
|
|
|
|
const GEnumValue * const enum_value =
|
|
g_enum_get_value_by_nick(enum_class, str);
|
|
if (enum_value)
|
|
out_value = enum_value->value;
|
|
g_type_class_unref(enum_class);
|
|
|
|
if (!enum_value)
|
|
return FALSE;
|
|
}
|
|
*out_value_ptr = out_value;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_flags(const gchar *str, GType type, guint *out_value_ptr)
|
|
{
|
|
gchar **tokens = NULL;
|
|
gint i, value, out_value = 0;
|
|
gboolean success = FALSE;
|
|
|
|
g_return_val_if_fail(out_value_ptr != NULL, FALSE);
|
|
|
|
if (str) {
|
|
tokens = g_strsplit(str, ",", 32);
|
|
if (!tokens)
|
|
return FALSE;
|
|
|
|
for (i = 0; tokens[i] != NULL; i++) {
|
|
if (!parse_enum(tokens[i], type, 0, &value))
|
|
goto end;
|
|
out_value |= value;
|
|
}
|
|
}
|
|
*out_value_ptr = out_value;
|
|
success = TRUE;
|
|
|
|
end:
|
|
g_strfreev(tokens);
|
|
return success;
|
|
}
|
|
|
|
static inline gboolean
|
|
parse_deinterlace(const gchar *str, GstVaapiDeinterlaceMethod *deinterlace_ptr)
|
|
{
|
|
return parse_enum(str, GST_VAAPI_TYPE_DEINTERLACE_METHOD,
|
|
GST_VAAPI_DEINTERLACE_METHOD_NONE, (gint *)deinterlace_ptr);
|
|
}
|
|
|
|
static inline gboolean
|
|
parse_deinterlace_flags(const gchar *str, guint *deinterlace_flags_ptr)
|
|
{
|
|
return parse_flags(str, GST_VAAPI_TYPE_DEINTERLACE_FLAGS,
|
|
deinterlace_flags_ptr);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
GstVaapiDisplay *display;
|
|
GstVaapiWindow *window;
|
|
GstVaapiSurface *src_surface, *dst_surface;
|
|
GstVaapiFilter *filter = NULL;
|
|
GstVaapiFilterStatus status;
|
|
GstVaapiDeinterlaceMethod deinterlace_method;
|
|
guint deinterlace_flags = 0;
|
|
guint filter_flags = 0;
|
|
guint surface_flags = 0;
|
|
gdouble denoise_level, sharpen_level;
|
|
GError *error = NULL;
|
|
|
|
static const guint src_width = 320;
|
|
static const guint src_height = 240;
|
|
static const guint dst_width = 480;
|
|
static const guint dst_height = 360;
|
|
static const guint win_width = 640;
|
|
static const guint win_height = 480;
|
|
|
|
if (!video_output_init(&argc, argv, g_options))
|
|
g_error("failed to initialize video output subsystem");
|
|
|
|
if (g_denoise_str && !parse_double(g_denoise_str, &denoise_level))
|
|
g_error("failed to parse noise reduction level");
|
|
|
|
if (g_sharpen_str && !parse_double(g_sharpen_str, &sharpen_level))
|
|
g_error("failed to parse sharpening level");
|
|
|
|
if (!parse_deinterlace(g_deinterlace_str, &deinterlace_method))
|
|
g_error("failed to parse deinterlace method `%s'", g_deinterlace_str);
|
|
|
|
if (!parse_deinterlace_flags(g_deinterlace_flags_str, &deinterlace_flags))
|
|
g_error("failed to parse deinterlace flags `%s'",
|
|
g_deinterlace_flags_str);
|
|
|
|
display = video_output_create_display(NULL);
|
|
if (!display)
|
|
g_error("failed to create VA display");
|
|
|
|
window = video_output_create_window(display, win_width, win_height);
|
|
if (!window)
|
|
g_error("failed to create window");
|
|
|
|
filter = gst_vaapi_filter_new(display);
|
|
if (!filter)
|
|
g_error("failed to create video processing pipeline");
|
|
|
|
dump_operations(filter);
|
|
dump_formats(filter);
|
|
|
|
if (g_crop_rect_str) {
|
|
GstVaapiRectangle crop_rect;
|
|
|
|
if (!parse_crop_rect(g_crop_rect_str, &crop_rect))
|
|
g_error("failed to parse cropping rectangle");
|
|
|
|
printf("Frame cropping: (%d,%d), size %ux%u\n",
|
|
crop_rect.x, crop_rect.y, crop_rect.width, crop_rect.height);
|
|
|
|
if (!gst_vaapi_filter_set_cropping_rectangle(filter, &crop_rect))
|
|
g_error("failed to set cropping rectangle");
|
|
}
|
|
|
|
if (g_denoise_str) {
|
|
printf("Noise reduction level: %f\n", denoise_level);
|
|
|
|
if (!gst_vaapi_filter_set_denoising_level(filter, denoise_level))
|
|
g_error("failed to set denoising level");
|
|
}
|
|
|
|
if (g_sharpen_str) {
|
|
printf("Sharpening level: %f\n", sharpen_level);
|
|
|
|
if (!gst_vaapi_filter_set_sharpening_level(filter, sharpen_level))
|
|
g_error("failed to set sharpening level");
|
|
}
|
|
|
|
if (deinterlace_method != GST_VAAPI_DEINTERLACE_METHOD_NONE) {
|
|
printf("Enable deinterlacing: %s\n", g_deinterlace_str);
|
|
|
|
if (!gst_vaapi_filter_set_deinterlacing(filter, deinterlace_method,
|
|
deinterlace_flags))
|
|
g_error("failed to set deinterlacing method");
|
|
}
|
|
else if (deinterlace_flags) {
|
|
if (deinterlace_flags & GST_VAAPI_DEINTERLACE_FLAG_TFF)
|
|
filter_flags = GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
|
|
else
|
|
filter_flags = GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
|
|
}
|
|
|
|
if (deinterlace_method != GST_VAAPI_DEINTERLACE_METHOD_NONE ||
|
|
deinterlace_flags) {
|
|
if (!(deinterlace_flags & GST_VAAPI_DEINTERLACE_FLAG_ONEFIELD))
|
|
surface_flags = GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD |
|
|
GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
|
|
else if (deinterlace_flags & GST_VAAPI_DEINTERLACE_FLAG_TFF)
|
|
surface_flags = GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
|
|
else
|
|
surface_flags = GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
|
|
}
|
|
|
|
src_surface = create_test_surface(display, src_width, src_height,
|
|
surface_flags, &error);
|
|
if (!src_surface)
|
|
g_error("failed to create source VA surface: %s", error->message);
|
|
|
|
dst_surface = gst_vaapi_surface_new(display, GST_VAAPI_CHROMA_TYPE_YUV420,
|
|
dst_width, dst_height);
|
|
if (!dst_surface)
|
|
g_error("failed to create target VA surface");
|
|
|
|
status = gst_vaapi_filter_process(filter, src_surface, dst_surface,
|
|
filter_flags);
|
|
if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
|
|
g_error("failed to process video filters");
|
|
|
|
gst_vaapi_window_show(window);
|
|
|
|
if (!gst_vaapi_window_put_surface(window, dst_surface, NULL, NULL,
|
|
GST_VAAPI_PICTURE_STRUCTURE_FRAME))
|
|
g_error("failed to render target surface");
|
|
|
|
pause();
|
|
|
|
gst_vaapi_filter_unref(filter);
|
|
gst_vaapi_object_unref(dst_surface);
|
|
gst_vaapi_object_unref(src_surface);
|
|
gst_vaapi_window_unref(window);
|
|
gst_vaapi_display_unref(display);
|
|
video_output_exit();
|
|
g_free(g_src_format_str);
|
|
g_free(g_crop_rect_str);
|
|
g_free(g_denoise_str);
|
|
g_free(g_sharpen_str);
|
|
g_free(g_deinterlace_str);
|
|
g_free(g_deinterlace_flags_str);
|
|
return 0;
|
|
}
|