Update
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
examples = [
|
||||
'test-vaapisink',
|
||||
'test-vaapipostproc',
|
||||
'test-roi',
|
||||
]
|
||||
|
||||
foreach example : examples
|
||||
executable(example, '@0@.c'.format(example),
|
||||
c_args : gstreamer_vaapi_args,
|
||||
include_directories: [configinc, libsinc],
|
||||
dependencies : [gst_dep, gstvideo_dep],
|
||||
install: false)
|
||||
endforeach
|
||||
|
||||
if USE_X11 and USE_WAYLAND
|
||||
if gtk_dep.found()
|
||||
executable('test-vaapicontext', 'test-vaapicontext.c',
|
||||
c_args : gstreamer_vaapi_args,
|
||||
include_directories: [configinc, libsinc],
|
||||
dependencies : [ gst_dep,
|
||||
gstvideo_dep,
|
||||
libva_dep,
|
||||
x11_dep,
|
||||
gtk_dep,
|
||||
libva_wayland_dep,
|
||||
libva_x11_dep ],
|
||||
install: false)
|
||||
endif
|
||||
endif
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* test-roi.c - Testsuite for Region of Interest
|
||||
*
|
||||
* Copyright (C) 2017 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 <stdio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/navigation.h>
|
||||
#include <gst/video/gstvideometa.h>
|
||||
|
||||
typedef struct _CustomData
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GMainLoop *loop;
|
||||
gboolean roi_enabled;
|
||||
} AppData;
|
||||
|
||||
static void
|
||||
send_eos_event (AppData * data)
|
||||
{
|
||||
gst_element_send_event (data->pipeline, gst_event_new_eos ());
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_keystroke (AppData * app, const gchar * str)
|
||||
{
|
||||
switch (g_ascii_tolower (str[0])) {
|
||||
case 'r':
|
||||
app->roi_enabled = !app->roi_enabled;
|
||||
gst_println ("ROI %s", app->roi_enabled ? "enabled" : "disabled");
|
||||
break;
|
||||
case 'q':
|
||||
send_eos_event (app);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
cb_msg (GstBus * bus, GstMessage * msg, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
GstNavigationMessageType mtype = gst_navigation_message_get_type (msg);
|
||||
GstEvent *ev = NULL;
|
||||
GstNavigationEventType type;
|
||||
const gchar *key;
|
||||
|
||||
if (mtype != GST_NAVIGATION_MESSAGE_EVENT)
|
||||
return;
|
||||
if (!gst_navigation_message_parse_event (msg, &ev))
|
||||
goto bail;
|
||||
|
||||
type = gst_navigation_event_get_type (ev);
|
||||
if (type != GST_NAVIGATION_EVENT_KEY_PRESS)
|
||||
goto bail;
|
||||
if (!gst_navigation_event_parse_key_event (ev, &key))
|
||||
goto bail;
|
||||
|
||||
dispatch_keystroke (app, key);
|
||||
|
||||
bail:
|
||||
if (ev)
|
||||
gst_event_unref (ev);
|
||||
}
|
||||
|
||||
static void
|
||||
cb_msg_eos (GstBus * bus, GstMessage * msg, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
g_main_loop_quit (app->loop);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cb_msg_error (GstBus * bus, GstMessage * msg, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
gchar *debug = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
gst_message_parse_error (msg, &err, &debug);
|
||||
|
||||
g_print ("Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
|
||||
if (debug) {
|
||||
g_print ("Debug details: %s\n", debug);
|
||||
g_free (debug);
|
||||
}
|
||||
|
||||
g_main_loop_quit (app->loop);
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
cb_add_roi (GstPad * pad, GstPadProbeInfo * info, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
GstVideoRegionOfInterestMeta *rmeta;
|
||||
GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info);
|
||||
GstStructure *s;
|
||||
|
||||
if (!app->roi_enabled)
|
||||
return GST_PAD_PROBE_OK;
|
||||
|
||||
buf = gst_buffer_make_writable (buf);
|
||||
if (!buf)
|
||||
return GST_PAD_PROBE_OK;
|
||||
|
||||
rmeta =
|
||||
gst_buffer_add_video_region_of_interest_meta (buf, "test", 0, 0, 320,
|
||||
240);
|
||||
if (!rmeta)
|
||||
return GST_PAD_PROBE_OK;
|
||||
|
||||
s = gst_structure_new ("roi/vaapi", "delta-qp", G_TYPE_INT, -10, NULL);
|
||||
gst_video_region_of_interest_meta_add_param (rmeta, s);
|
||||
|
||||
GST_PAD_PROBE_INFO_DATA (info) = buf;
|
||||
return GST_PAD_PROBE_OK;
|
||||
}
|
||||
|
||||
/* Process keyboard input */
|
||||
static gboolean
|
||||
handle_keyboard (GIOChannel * source, GIOCondition cond, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
gchar *str = NULL;
|
||||
|
||||
if (g_io_channel_read_line (source, &str, NULL, NULL,
|
||||
NULL) != G_IO_STATUS_NORMAL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
dispatch_keystroke (app, str);
|
||||
|
||||
g_free (str);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an example pipeline to recognize difference between ROI and non-ROI.
|
||||
* 1. Produce snow pattern with 320p
|
||||
* 2. Encode and decode the raw data with 2 pipelines at same time.
|
||||
* 2.1. Insert GstVideoRegionOfInterestMeta to the 2nd pipeline buffers to enable ROI.
|
||||
* 3. Mix both streams in videomixer.
|
||||
* 5. Output the result in one window.
|
||||
*
|
||||
* Note that the higher definition of original raw data, the easier we
|
||||
* recognize. So you can replace videotestsrc with your
|
||||
* high-definition camera or other src elements.
|
||||
*/
|
||||
|
||||
/*
|
||||
.----------. .---. .--------. .---. .---. .---. .--------. .----------. .-----.
|
||||
| videosrc |->|tee|->Q->|txtovrly|->|enc|->|dec|->|vpp|->|videobox|->|videomixer|->|vsink|
|
||||
'----------' '---' '--------' '---' '---' '---' '--------' '----------' '-----'
|
||||
^ ^
|
||||
| |
|
||||
| .--------. .---. .---. .---. .--------. |
|
||||
'--->Q->|txtovrly|->|enc|->|dec|->|vpp|->|videobox|->'
|
||||
^ '--------' '---' '---' '---' '--------'
|
||||
|
|
||||
'-- Insert GstVideoRegionOfInterestMeta width roit/vaapi params on buffers
|
||||
*/
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
AppData data = { 0, };
|
||||
GstStateChangeReturn ret;
|
||||
GstElement *el;
|
||||
GstPad *pad;
|
||||
GError *err = NULL;
|
||||
GIOChannel *io_stdin;
|
||||
GstBus *bus;
|
||||
|
||||
data.roi_enabled = TRUE;
|
||||
|
||||
/* Initialize GStreamer */
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
/* Print usage map */
|
||||
g_print ("USAGE: 'r' to enable/disable ROI && 'q' to quit\n");
|
||||
|
||||
#define SRC "videotestsrc pattern=snow ! " \
|
||||
"video/x-raw, format=NV12, width=320, framerate=5/1"
|
||||
#define ENCDEC "vaapih265enc rate-control=cbr bitrate=2000 ! vaapih265dec ! " \
|
||||
"vaapipostproc ! video/x-raw, width=640"
|
||||
#define TEXT "textoverlay font-desc=\"Arial Bold 48\" "
|
||||
|
||||
data.pipeline =
|
||||
gst_parse_launch
|
||||
("videomixer name=mix ! vaapipostproc ! vaapisink sync=false "
|
||||
SRC " ! tee name=t ! queue ! " TEXT " text=\"non-ROI\" ! " ENCDEC
|
||||
" ! videobox left=-640 ! mix. "
|
||||
" t. ! queue name=roi ! " TEXT " text=\"ROI\" ! " ENCDEC
|
||||
" ! videobox ! mix.", &err);
|
||||
|
||||
if (err) {
|
||||
g_printerr ("failed to parse pipeline: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bus = gst_pipeline_get_bus (GST_PIPELINE (data.pipeline));
|
||||
gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
|
||||
gst_bus_enable_sync_message_emission (bus);
|
||||
g_signal_connect (bus, "message::error", G_CALLBACK (cb_msg_error), &data);
|
||||
g_signal_connect (bus, "message::eos", G_CALLBACK (cb_msg_eos), &data);
|
||||
g_signal_connect (bus, "message::element", G_CALLBACK (cb_msg), &data);
|
||||
gst_object_unref (bus);
|
||||
|
||||
el = gst_bin_get_by_name (GST_BIN (data.pipeline), "roi");
|
||||
pad = gst_element_get_static_pad (el, "src");
|
||||
gst_object_unref (el);
|
||||
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, cb_add_roi, &data, NULL);
|
||||
gst_object_unref (pad);
|
||||
|
||||
/* Add a keyboard watch so we get notified of keystrokes */
|
||||
io_stdin = g_io_channel_unix_new (fileno (stdin));
|
||||
g_io_add_watch (io_stdin, G_IO_IN, handle_keyboard, &data);
|
||||
|
||||
/* Start playing */
|
||||
ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
g_printerr ("Unable to set the pipeline to the playing state.\n");
|
||||
gst_object_unref (data.pipeline);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create a GLib Main Loop and set it to run */
|
||||
data.loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (data.loop);
|
||||
|
||||
/* Free resources */
|
||||
g_main_loop_unref (data.loop);
|
||||
gst_element_set_state (data.pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (data.pipeline);
|
||||
g_io_channel_unref (io_stdin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* test-vaapicontext.c - Testsuite for VAAPI app context
|
||||
*
|
||||
* Copyright (C) 2017 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/gst.h>
|
||||
#include <gst/video/videooverlay.h>
|
||||
|
||||
#include <va/va.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include <X11/Xlib.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <va/va_x11.h>
|
||||
#endif
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <va/va_wayland.h>
|
||||
#endif
|
||||
|
||||
static gboolean g_multisink;
|
||||
static gchar *g_filepath;
|
||||
static GstElement *g_vaapisink;
|
||||
|
||||
static GOptionEntry g_options[] = {
|
||||
{"multi", 'm', 0, G_OPTION_ARG_NONE, &g_multisink, "test multiple vaapisink",
|
||||
NULL},
|
||||
{"file", 'f', 0, G_OPTION_ARG_STRING, &g_filepath, "file path to play", NULL},
|
||||
{NULL,}
|
||||
};
|
||||
|
||||
typedef struct _CustomData
|
||||
{
|
||||
GtkWidget *main_window;
|
||||
VADisplay va_display;
|
||||
GstElement *pipeline;
|
||||
guintptr videoarea_handle[2];
|
||||
GstObject *gstvaapidisplay;
|
||||
GtkWidget *video_widget[2];
|
||||
GstVideoOverlay *overlay[2];
|
||||
} AppData;
|
||||
|
||||
static void
|
||||
delete_event_cb (GtkWidget * widget, GdkEvent * event, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
|
||||
gst_element_set_state (app->pipeline, GST_STATE_NULL);
|
||||
gtk_main_quit ();
|
||||
}
|
||||
|
||||
static void
|
||||
button_rotate_cb (GtkWidget * widget, GstElement * elem)
|
||||
{
|
||||
static gint counter = 0;
|
||||
const static gint tags[] = { 90, 180, 270, 0 };
|
||||
|
||||
g_object_set (elem, "rotation", tags[counter++ % G_N_ELEMENTS (tags)], NULL);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
get_native_display (AppData * app, gboolean * is_x11)
|
||||
{
|
||||
GdkDisplay *gdk_display;
|
||||
|
||||
gdk_display = gtk_widget_get_display (app->main_window);
|
||||
|
||||
#if defined(GDK_WINDOWING_X11)
|
||||
if (GDK_IS_X11_DISPLAY (gdk_display)) {
|
||||
*is_x11 = TRUE;
|
||||
return gdk_x11_display_get_xdisplay (gdk_display);
|
||||
} else
|
||||
#endif
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
|
||||
*is_x11 = FALSE;
|
||||
return gdk_wayland_display_get_wl_display (gdk_display);
|
||||
} else
|
||||
#endif
|
||||
g_error ("Running in a non supported environment");
|
||||
}
|
||||
|
||||
static VADisplay
|
||||
ensure_va_display (AppData * app, gpointer native_display, gboolean is_x11)
|
||||
{
|
||||
if (app->va_display)
|
||||
return app->va_display;
|
||||
app->va_display = is_x11 ?
|
||||
vaGetDisplay (native_display) : vaGetDisplayWl (native_display);
|
||||
/* There's no need to call vaInitialize() since element does it
|
||||
* internally */
|
||||
return app->va_display;
|
||||
}
|
||||
|
||||
static GstContext *
|
||||
create_vaapi_app_display_context (AppData * app, gboolean new_va_display)
|
||||
{
|
||||
GstContext *context;
|
||||
GstStructure *s;
|
||||
VADisplay va_display;
|
||||
gpointer native_display = NULL;
|
||||
const gchar *name = NULL;
|
||||
gboolean is_x11;
|
||||
|
||||
native_display = get_native_display (app, &is_x11);
|
||||
|
||||
if (new_va_display) {
|
||||
va_display = is_x11 ?
|
||||
vaGetDisplay (native_display) : vaGetDisplayWl (native_display);
|
||||
} else
|
||||
va_display = ensure_va_display (app, native_display, is_x11);
|
||||
|
||||
name = is_x11 ? "x11-display" : "wl-display";
|
||||
|
||||
context = gst_context_new ("gst.vaapi.app.Display", FALSE);
|
||||
s = gst_context_writable_structure (context);
|
||||
gst_structure_set (s, "va-display", G_TYPE_POINTER, va_display, NULL);
|
||||
gst_structure_set (s, name, G_TYPE_POINTER, native_display, NULL);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static void
|
||||
get_allocation (GtkWidget * widget, GtkAllocation * allocation)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
|
||||
gtk_widget_get_allocation (widget, allocation);
|
||||
|
||||
/* On Wayland the whole gtk window is one surface and the video is a
|
||||
* subsurface of the top-level surface. So the position must be relative
|
||||
* to the top-level window not relative to the parent widget */
|
||||
if (GDK_IS_WAYLAND_DISPLAY (display))
|
||||
gtk_widget_translate_coordinates (widget, gtk_widget_get_toplevel (widget),
|
||||
0, 0, &allocation->x, &allocation->y);
|
||||
}
|
||||
|
||||
static GstBusSyncReply
|
||||
bus_sync_handler (GstBus * bus, GstMessage * msg, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (msg)) {
|
||||
case GST_MESSAGE_NEED_CONTEXT:{
|
||||
const gchar *context_type;
|
||||
gboolean new_va_disp;
|
||||
GstContext *context;
|
||||
|
||||
gst_message_parse_context_type (msg, &context_type);
|
||||
gst_println ("Got need context %s from %s", context_type,
|
||||
GST_MESSAGE_SRC_NAME (msg));
|
||||
|
||||
if (g_strcmp0 (context_type, "gst.vaapi.Display") == 0) {
|
||||
if (app->gstvaapidisplay) {
|
||||
GstStructure *s;
|
||||
|
||||
context = gst_context_new ("gst.vaapi.Display", FALSE);
|
||||
s = gst_context_writable_structure (context);
|
||||
gst_structure_set (s, "gst.vaapi.Display",
|
||||
GST_TYPE_OBJECT, app->gstvaapidisplay, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_strcmp0 (context_type, "gst.vaapi.app.Display") != 0)
|
||||
break;
|
||||
|
||||
/* create a new VA display *only* for the second video sink */
|
||||
new_va_disp = (g_strcmp0 (GST_MESSAGE_SRC_NAME (msg), "sink2") == 0);
|
||||
|
||||
context = create_vaapi_app_display_context (app, new_va_disp);
|
||||
gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), context);
|
||||
gst_context_unref (context);
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_ELEMENT:{
|
||||
GstVideoOverlay *overlay;
|
||||
GtkAllocation allocation;
|
||||
guint i;
|
||||
|
||||
if (!gst_is_video_overlay_prepare_window_handle_message (msg))
|
||||
break;
|
||||
|
||||
overlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (msg));
|
||||
|
||||
i = (g_strcmp0 (GST_MESSAGE_SRC_NAME (msg), "sink2") == 0) ? 1 : 0;
|
||||
|
||||
app->overlay[i] = overlay;
|
||||
get_allocation (app->video_widget[i], &allocation);
|
||||
gst_video_overlay_set_window_handle (overlay, app->videoarea_handle[i]);
|
||||
gtk_widget_queue_draw_area (app->video_widget[i], 0, 0, allocation.width,
|
||||
allocation.height);
|
||||
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_HAVE_CONTEXT:{
|
||||
const gchar *context_type;
|
||||
const GstStructure *s;
|
||||
GstContext *context = NULL;
|
||||
const GValue *value;
|
||||
|
||||
gst_message_parse_have_context (msg, &context);
|
||||
if (!context)
|
||||
break;
|
||||
|
||||
context_type = gst_context_get_context_type (context);
|
||||
gst_println ("Got have context %s from %s", context_type,
|
||||
GST_MESSAGE_SRC_NAME (msg));
|
||||
|
||||
if (g_strcmp0 (context_type, "gst.vaapi.Display") != 0)
|
||||
break;
|
||||
s = gst_context_get_structure (context);
|
||||
if (!s)
|
||||
break;
|
||||
value = gst_structure_get_value (s, "gst.vaapi.Display");
|
||||
if (!value)
|
||||
break;
|
||||
app->gstvaapidisplay = g_value_dup_object (value);
|
||||
gst_println ("found display %s", GST_OBJECT_NAME (app->gstvaapidisplay));
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_EOS:
|
||||
gtk_main_quit ();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BUS_PASS;
|
||||
}
|
||||
|
||||
static void
|
||||
play_cb (GtkButton * button, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
|
||||
gst_element_set_state (app->pipeline, GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
static void
|
||||
null_cb (GtkButton * button, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
|
||||
gst_element_set_state (app->pipeline, GST_STATE_NULL);
|
||||
app->va_display = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
realize_cb (GtkWidget * widget, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
GdkWindow *window;
|
||||
GdkDisplay *display;
|
||||
static guint counter = 0;
|
||||
|
||||
display = gdk_display_get_default ();
|
||||
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
/* On wayland gtk_widget_get_window() only works correctly for the
|
||||
* toplevel widget. Otherwise a new wayland surface is created but
|
||||
* never used and the video remains invisible. */
|
||||
if (GDK_IS_WAYLAND_DISPLAY (display))
|
||||
window = gtk_widget_get_window (app->main_window);
|
||||
else
|
||||
#endif
|
||||
window = gtk_widget_get_window (widget);
|
||||
|
||||
if (!gdk_window_ensure_native (window))
|
||||
g_error ("Couldn't create native window needed for GstOverlay!");
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
if (GDK_IS_X11_DISPLAY (display)) {
|
||||
app->videoarea_handle[counter++ % 2] = GDK_WINDOW_XID (window);
|
||||
} else
|
||||
#endif
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (display)) {
|
||||
app->videoarea_handle[counter++ % 2] =
|
||||
(guintptr) gdk_wayland_window_get_wl_surface (window);
|
||||
} else
|
||||
#endif
|
||||
g_error ("Unsupported GDK backend");
|
||||
}
|
||||
|
||||
static void
|
||||
draw_cb (GtkWidget * widget, cairo_t * cr, gpointer data)
|
||||
{
|
||||
AppData *app = data;
|
||||
GtkAllocation allocation;
|
||||
int i;
|
||||
|
||||
i = (widget == app->video_widget[0]) ? 0 : 1;
|
||||
|
||||
get_allocation (widget, &allocation);
|
||||
|
||||
gst_println ("draw_cb[%d] x %d, y %d, w %d, h %d\n", i,
|
||||
allocation.x, allocation.y, allocation.width, allocation.height);
|
||||
|
||||
if (app->overlay[i]) {
|
||||
gst_video_overlay_set_render_rectangle (app->overlay[i], allocation.x,
|
||||
allocation.y, allocation.width, allocation.height);
|
||||
}
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_video_box (AppData * app)
|
||||
{
|
||||
GtkWidget *video_area;
|
||||
|
||||
video_area = gtk_drawing_area_new ();
|
||||
gtk_widget_set_size_request (video_area, 640, 480);
|
||||
g_signal_connect (video_area, "realize", G_CALLBACK (realize_cb), app);
|
||||
g_signal_connect (video_area, "draw", G_CALLBACK (draw_cb), app);
|
||||
return video_area;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_rotate_button (AppData * app, const gchar * name)
|
||||
{
|
||||
GtkWidget *rotate;
|
||||
GstElement *sink;
|
||||
|
||||
sink = gst_bin_get_by_name (GST_BIN (app->pipeline), name);
|
||||
if (!sink && !g_strcmp0 (name, "sink1"))
|
||||
sink = g_vaapisink;
|
||||
g_assert (sink);
|
||||
|
||||
rotate = gtk_button_new_with_label ("Rotate");
|
||||
g_signal_connect (rotate, "clicked", G_CALLBACK (button_rotate_cb), sink);
|
||||
if (sink != g_vaapisink)
|
||||
gst_object_unref (sink);
|
||||
|
||||
return rotate;
|
||||
}
|
||||
|
||||
static void
|
||||
build_ui (AppData * app)
|
||||
{
|
||||
GtkWidget *mainwin, *vbox, *pane, *bbox;
|
||||
|
||||
mainwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title (GTK_WINDOW (mainwin), "VAAPI display context test");
|
||||
gtk_window_set_resizable (GTK_WINDOW (mainwin), FALSE);
|
||||
g_signal_connect (mainwin, "delete-event", G_CALLBACK (delete_event_cb), app);
|
||||
app->main_window = mainwin;
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (mainwin), vbox);
|
||||
|
||||
pane = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), pane, TRUE, TRUE, 0);
|
||||
|
||||
/* first video box */
|
||||
app->video_widget[0] = create_video_box (app);
|
||||
gtk_paned_pack1 (GTK_PANED (pane), app->video_widget[0], TRUE, TRUE);
|
||||
|
||||
/* rotate buttons */
|
||||
bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), bbox, TRUE, TRUE, 0);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (bbox), create_rotate_button (app, "sink1"), TRUE,
|
||||
TRUE, 0);
|
||||
|
||||
if (g_multisink) {
|
||||
/* second video box */
|
||||
app->video_widget[1] = create_video_box (app);
|
||||
gtk_paned_pack2 (GTK_PANED (pane), app->video_widget[1], TRUE, TRUE);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (bbox), create_rotate_button (app, "sink2"),
|
||||
TRUE, TRUE, 0);
|
||||
} else {
|
||||
GtkWidget *button;
|
||||
|
||||
button = gtk_button_new_with_label ("PLAYING");
|
||||
gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (play_cb), app);
|
||||
|
||||
button = gtk_button_new_with_label ("NULL");
|
||||
gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (null_cb), app);
|
||||
}
|
||||
|
||||
gtk_widget_show_all (mainwin);
|
||||
}
|
||||
|
||||
int
|
||||
main (gint argc, gchar ** argv)
|
||||
{
|
||||
AppData app = { 0, };
|
||||
GstBus *bus;
|
||||
GOptionContext *ctx;
|
||||
GError *error = NULL;
|
||||
|
||||
XInitThreads ();
|
||||
|
||||
ctx = g_option_context_new ("- test options");
|
||||
if (!ctx)
|
||||
return -1;
|
||||
|
||||
g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
|
||||
g_option_context_add_group (ctx, gst_init_get_option_group ());
|
||||
g_option_context_add_main_entries (ctx, g_options, NULL);
|
||||
if (!g_option_context_parse (ctx, &argc, &argv, NULL))
|
||||
return -1;
|
||||
g_option_context_free (ctx);
|
||||
|
||||
if (g_multisink) {
|
||||
app.pipeline = gst_parse_launch ("videotestsrc ! tee name=t ! queue ! "
|
||||
"vaapisink name=sink1 t. ! queue ! vaapisink name=sink2", &error);
|
||||
} else if (!g_filepath) {
|
||||
app.pipeline = gst_parse_launch ("videotestsrc ! vaapih264enc ! "
|
||||
"vaapidecodebin ! vaapisink name=sink1", &error);
|
||||
} else {
|
||||
app.pipeline = gst_element_factory_make ("playbin", NULL);
|
||||
g_assert (app.pipeline);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
gst_printerrln ("failed to parse pipeline: %s", error->message);
|
||||
g_error_free (error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_multisink && g_filepath) {
|
||||
g_vaapisink = gst_element_factory_make ("vaapisink", "sink1");
|
||||
g_assert (g_vaapisink);
|
||||
g_object_set (app.pipeline, "uri", g_filepath, "video-sink", g_vaapisink,
|
||||
NULL);
|
||||
}
|
||||
|
||||
build_ui (&app);
|
||||
|
||||
bus = gst_element_get_bus (app.pipeline);
|
||||
gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) & app, NULL);
|
||||
gst_object_unref (bus);
|
||||
|
||||
gst_element_set_state (app.pipeline, GST_STATE_PLAYING);
|
||||
gst_println ("Now playing…");
|
||||
|
||||
gtk_main ();
|
||||
|
||||
gst_object_unref (app.pipeline);
|
||||
gst_object_unref (app.gstvaapidisplay);
|
||||
/* there is no need to call vaTerminate() because it is done by the
|
||||
* vaapi elements */
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* test-vaapipostproc.c - Testsuite for VAAPI Postprocessor
|
||||
*
|
||||
* Copyright (C) 2017 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 <stdio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
|
||||
typedef struct _CustomData
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *postproc;
|
||||
GMainLoop *loop;
|
||||
} AppData;
|
||||
|
||||
static gboolean
|
||||
_check_passthrough_mode (gpointer user_data)
|
||||
{
|
||||
gboolean ret;
|
||||
AppData *data = (AppData *) user_data;
|
||||
|
||||
ret = gst_base_transform_is_passthrough (GST_BASE_TRANSFORM (data->postproc));
|
||||
|
||||
if (ret)
|
||||
gst_println ("Now this pipeline is on passthrough mode");
|
||||
else
|
||||
gst_println ("Now this pipeline is NOT on passthrough mode");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
set_contrast (AppData * data)
|
||||
{
|
||||
static gfloat value = 1.0;
|
||||
|
||||
value = value == 1.0 ? 0.5 : 1.0;
|
||||
g_object_set (data->postproc, "contrast", value, NULL);
|
||||
gst_println ("contrast value is changed to %f", value);
|
||||
|
||||
g_timeout_add (300, _check_passthrough_mode, data);
|
||||
}
|
||||
|
||||
static void
|
||||
change_size (AppData * data)
|
||||
{
|
||||
static gint i = 0;
|
||||
if (i == 0) {
|
||||
g_object_set (data->postproc, "width", 1280, "height", 720, NULL);
|
||||
gst_println ("frame size is changed to 1280x720");
|
||||
i++;
|
||||
} else {
|
||||
g_object_set (data->postproc, "width", 0, "height", 0, NULL);
|
||||
gst_println ("frame size is changed to default");
|
||||
i = 0;
|
||||
}
|
||||
|
||||
g_timeout_add (300, _check_passthrough_mode, data);
|
||||
}
|
||||
|
||||
/* Process keyboard input */
|
||||
static gboolean
|
||||
handle_keyboard (GIOChannel * source, GIOCondition cond, AppData * data)
|
||||
{
|
||||
gchar *str = NULL;
|
||||
|
||||
if (g_io_channel_read_line (source, &str, NULL, NULL,
|
||||
NULL) != G_IO_STATUS_NORMAL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (g_ascii_tolower (str[0])) {
|
||||
case 's':{
|
||||
set_contrast (data);
|
||||
break;
|
||||
}
|
||||
case 'c':{
|
||||
change_size (data);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
g_main_loop_quit (data->loop);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
AppData data = { 0, };
|
||||
GstStateChangeReturn ret;
|
||||
GIOChannel *io_stdin;
|
||||
|
||||
/* Initialize GStreamer */
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
/* Print usage map */
|
||||
gst_println ("USAGE: Choose one of the following options, then press enter:\n"
|
||||
" 's' to set contrast\n" " 'c' to change size\n" " 'q' to quit\n");
|
||||
|
||||
data.pipeline =
|
||||
gst_parse_launch
|
||||
("videotestsrc name=src ! vaapih264enc ! vaapih264dec ! vaapipostproc name=postproc ! vaapisink",
|
||||
NULL);
|
||||
data.postproc = gst_bin_get_by_name (GST_BIN (data.pipeline), "postproc");
|
||||
|
||||
/* Add a keyboard watch so we get notified of keystrokes */
|
||||
io_stdin = g_io_channel_unix_new (fileno (stdin));
|
||||
g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);
|
||||
|
||||
/* Start playing */
|
||||
ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
g_printerr ("Unable to set the pipeline to the playing state.\n");
|
||||
gst_object_unref (data.pipeline);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_timeout_add (300, _check_passthrough_mode, &data);
|
||||
|
||||
/* Create a GLib Main Loop and set it to run */
|
||||
data.loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (data.loop);
|
||||
|
||||
/* Free resources */
|
||||
g_main_loop_unref (data.loop);
|
||||
g_io_channel_unref (io_stdin);
|
||||
gst_element_set_state (data.pipeline, GST_STATE_NULL);
|
||||
|
||||
gst_object_unref (data.postproc);
|
||||
gst_object_unref (data.pipeline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
static gboolean use_postproc;
|
||||
static GOptionEntry g_options[] = {
|
||||
{"postproc", 'p', 0, G_OPTION_ARG_NONE, &use_postproc,
|
||||
"use vaapipostproc to rotate rather than vaapisink", NULL},
|
||||
{NULL,}
|
||||
};
|
||||
|
||||
typedef struct _CustomData
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *rotator;
|
||||
GMainLoop *loop;
|
||||
gboolean orient_automatic;
|
||||
} AppData;
|
||||
|
||||
static void
|
||||
send_rotate_event (AppData * data)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstEvent *event;
|
||||
static gint counter = 0;
|
||||
const static gchar *tags[] = { "rotate-90", "rotate-180", "rotate-270",
|
||||
"rotate-0", "flip-rotate-0", "flip-rotate-90", "flip-rotate-180",
|
||||
"flip-rotate-270",
|
||||
};
|
||||
|
||||
event = gst_event_new_tag (gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION,
|
||||
tags[counter++ % G_N_ELEMENTS (tags)], NULL));
|
||||
|
||||
/* Send the event */
|
||||
g_print ("Sending event %" GST_PTR_FORMAT ": ", event);
|
||||
res = gst_element_send_event (data->pipeline, event);
|
||||
g_print ("%s\n", res ? "ok" : "failed");
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_cb (const gchar * key, AppData * data)
|
||||
{
|
||||
switch (g_ascii_tolower (key[0])) {
|
||||
case 'r':
|
||||
send_rotate_event (data);
|
||||
break;
|
||||
case 's':{
|
||||
if (use_postproc) {
|
||||
g_object_set (G_OBJECT (data->rotator), "video-direction",
|
||||
GST_VIDEO_ORIENTATION_AUTO, NULL);
|
||||
} else {
|
||||
/* rotation=360 means auto for vaapisnk */
|
||||
g_object_set (G_OBJECT (data->rotator), "rotation", 360, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
g_main_loop_quit (data->loop);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data)
|
||||
{
|
||||
AppData *data = user_data;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (msg)) {
|
||||
case GST_MESSAGE_ELEMENT:
|
||||
{
|
||||
GstNavigationMessageType mtype = gst_navigation_message_get_type (msg);
|
||||
if (mtype == GST_NAVIGATION_MESSAGE_EVENT) {
|
||||
GstEvent *ev = NULL;
|
||||
|
||||
if (gst_navigation_message_parse_event (msg, &ev)) {
|
||||
GstNavigationEventType type = gst_navigation_event_get_type (ev);
|
||||
if (type == GST_NAVIGATION_EVENT_KEY_PRESS) {
|
||||
const gchar *key;
|
||||
|
||||
if (gst_navigation_event_parse_key_event (ev, &key))
|
||||
keyboard_cb (key, data);
|
||||
}
|
||||
}
|
||||
if (ev)
|
||||
gst_event_unref (ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Process keyboard input */
|
||||
static gboolean
|
||||
handle_keyboard (GIOChannel * source, GIOCondition cond, AppData * data)
|
||||
{
|
||||
gchar *str = NULL;
|
||||
|
||||
if (g_io_channel_read_line (source, &str, NULL, NULL,
|
||||
NULL) != G_IO_STATUS_NORMAL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
keyboard_cb (str, data);
|
||||
g_free (str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
AppData data;
|
||||
GstStateChangeReturn ret;
|
||||
GIOChannel *io_stdin;
|
||||
GOptionContext *ctx;
|
||||
GError *err = NULL;
|
||||
guint srcid;
|
||||
|
||||
/* Initialize GStreamer */
|
||||
ctx = g_option_context_new ("- test options");
|
||||
if (!ctx)
|
||||
return -1;
|
||||
g_option_context_add_group (ctx, gst_init_get_option_group ());
|
||||
g_option_context_add_main_entries (ctx, g_options, NULL);
|
||||
if (!g_option_context_parse (ctx, &argc, &argv, NULL))
|
||||
return -1;
|
||||
g_option_context_free (ctx);
|
||||
|
||||
/* Print usage map */
|
||||
g_print ("USAGE: Choose one of the following options, then press enter:\n"
|
||||
" 'r' to send image-orientation tag event\n"
|
||||
" 's' to set orient-automatic\n" " 'Q' to quit\n");
|
||||
|
||||
if (use_postproc) {
|
||||
data.pipeline =
|
||||
gst_parse_launch ("videotestsrc ! vaapipostproc name=pp ! xvimagesink",
|
||||
&err);
|
||||
} else {
|
||||
data.pipeline =
|
||||
gst_parse_launch ("videotestsrc ! vaapisink name=sink", &err);
|
||||
}
|
||||
if (err) {
|
||||
g_printerr ("failed to create pipeline: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (use_postproc)
|
||||
data.rotator = gst_bin_get_by_name (GST_BIN (data.pipeline), "pp");
|
||||
else
|
||||
data.rotator = gst_bin_get_by_name (GST_BIN (data.pipeline), "sink");
|
||||
srcid = gst_bus_add_watch (GST_ELEMENT_BUS (data.pipeline), bus_msg, &data);
|
||||
|
||||
/* Add a keyboard watch so we get notified of keystrokes */
|
||||
io_stdin = g_io_channel_unix_new (fileno (stdin));
|
||||
g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);
|
||||
|
||||
/* Start playing */
|
||||
ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
g_printerr ("Unable to set the pipeline to the playing state.\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Create a GLib Main Loop and set it to run */
|
||||
data.loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (data.loop);
|
||||
|
||||
gst_element_set_state (data.pipeline, GST_STATE_NULL);
|
||||
|
||||
bail:
|
||||
/* Free resources */
|
||||
g_source_remove (srcid);
|
||||
g_main_loop_unref (data.loop);
|
||||
g_io_channel_unref (io_stdin);
|
||||
|
||||
gst_object_unref (data.rotator);
|
||||
gst_object_unref (data.pipeline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user