This commit is contained in:
Akkariin Meiko
2022-03-12 03:16:09 +08:00
Unverified
parent 12b76e0c7a
commit 27c4ec74a1
10075 changed files with 5122287 additions and 1 deletions
@@ -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;
}