From: Matthew Waters Date: Tue, 29 Apr 2014 06:38:55 +0000 (+1000) Subject: gl/examples: move to -bad X-Git-Tag: 1.19.3~511^2~1989^2~1544 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=05bfd277a8058648660db47f102a313565f1089d;p=platform%2Fupstream%2Fgstreamer.git gl/examples: move to -bad - fix all the compiler errors - give them their own gl directory --- diff --git a/tests/examples/gl/Makefile.am b/tests/examples/gl/Makefile.am new file mode 100644 index 0000000..8d7d9f7 --- /dev/null +++ b/tests/examples/gl/Makefile.am @@ -0,0 +1,26 @@ + +SUBDIRS = + +if USE_OPENGL + +if HAVE_WINDOW_COCOA +SUBDIRS += cocoa +else + +SUBDIRS += generic qt + +#if HAVE_CLUTTER +#SUBDIRS += clutter +#endif + +if HAVE_SDL +SUBDIRS += sdl +endif + +if HAVE_GTK3 +SUBDIRS += gtk +endif + +endif + +endif diff --git a/tests/examples/gl/clutter/.gitignore b/tests/examples/gl/clutter/.gitignore new file mode 100644 index 0000000..9b1eb53 --- /dev/null +++ b/tests/examples/gl/clutter/.gitignore @@ -0,0 +1,3 @@ +clutteractor +clutteractortee +cluttershare diff --git a/tests/examples/gl/clutter/Makefile.am b/tests/examples/gl/clutter/Makefile.am new file mode 100644 index 0000000..0383a6a --- /dev/null +++ b/tests/examples/gl/clutter/Makefile.am @@ -0,0 +1,45 @@ +noinst_PROGRAMS = ## + +#works on win32 and X +if HAVE_CLUTTER + +noinst_PROGRAMS += cluttershare + +cluttershare_SOURCES = cluttershare.c + +cluttershare_CFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) $(GL_CFLAGS) $(CLUTTER_CFLAGS) +cluttershare_LDADD=$(CLUTTER_LIBS) $(GST_PLUGINS_BASE_LIBS) \ + $(GST_LIBS) $(GL_LIBS) \ + -lgstvideo-$(GST_API_VERSION) $(top_builddir)/gst-libs/gst/gl/libgstgl-@GST_API_VERSION@.la + +endif + + +if HAVE_CLUTTER_GLX +if HAVE_CLUTTER_X11 +if HAVE_XCOMPOSITE + +noinst_PROGRAMS += clutteractor clutteractortee + +clutteractor_SOURCES = clutteractor.c + +clutteractor_CFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ + $(GL_CFLAGS) $(CLUTTER_CFLAGS) \ + $(CLUTTER_GLX_CFLAGS) $(CLUTTER_X11_CFLAGS) $(XCOMPOSITE_CFLAGS) +clutteractor_LDADD=$(CLUTTER_LIBS) $(CLUTTER_GLX_LIBS) $(CLUTTER_X11_LIBS) \ + $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) $(XCOMPOSITE_LIBS) -lgstvideo-$(GST_API_VERSION) + +clutteractortee_SOURCES = clutteractortee.c + +clutteractortee_CFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ + $(GL_CFLAGS) $(CLUTTER_CFLAGS) \ + $(CLUTTER_GLX_CFLAGS) $(CLUTTER_X11_CFLAGS) $(XCOMPOSITE_CFLAGS) +clutteractortee_LDADD=$(CLUTTER_LIBS) $(CLUTTER_GLX_LIBS) $(CLUTTER_X11_LIBS) \ + $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) $(XCOMPOSITE_LIBS) -lgstvideo-$(GST_API_VERSION) + +endif +endif +endif diff --git a/tests/examples/gl/clutter/clutteractor.c b/tests/examples/gl/clutter/clutteractor.c new file mode 100644 index 0000000..44c97ba --- /dev/null +++ b/tests/examples/gl/clutter/clutteractor.c @@ -0,0 +1,178 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define CLUTTER_VERSION_MIN_REQUIRED CLUTTER_VERSION_1_8 + +#include +#include +#include +#include +#include +#include +#include + +#define W 320 +#define H 240 + +struct GstGLClutterActor_ +{ + Window win; + Window root; + ClutterActor *texture; + ClutterActor *stage; +}; + +typedef struct GstGLClutterActor_ GstGLClutterActor; + +static gboolean +create_actor (GstGLClutterActor * actor) +{ + //ClutterKnot knot[2]; + //ClutterTimeline *timeline; + ClutterAnimation *animation = NULL; + + actor->texture = g_object_new (CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, + "window", actor->win, "automatic-updates", TRUE, NULL); + clutter_container_add_actor (CLUTTER_CONTAINER (actor->stage), + actor->texture); + clutter_actor_set_scale (actor->texture, 0.2, 0.2); + clutter_actor_set_opacity (actor->texture, 0); + clutter_actor_show (actor->texture); + + //timeline = + // clutter_timeline_new (120 /* frames */ , 50 /* frames per second. */ ); + //clutter_timeline_set_loop (timeline, TRUE); + //clutter_timeline_start (timeline); + + /* Instead of our custom callback, + * we could use a standard callback. For instance, CLUTTER_ALPHA_SINE_INC. + */ + /*effect_template = + clutter_effect_template_new (timeline, CLUTTER_ALPHA_SINE_INC); */ + animation = + clutter_actor_animate (actor->texture, CLUTTER_LINEAR, 2400, + "x", 100.0, "y", 100.0, "opacity", 0, NULL); + + /* knot[0].x = -10; + knot[0].y = -10; + knot[1].x = 160; + knot[1].y = 120; */ + + // Move the actor along the path: + /* clutter_effect_path (effect_template, actor->texture, knot, + sizeof (knot) / sizeof (ClutterKnot), NULL, NULL); + clutter_effect_scale (effect_template, actor->texture, 1.0, 1.0, NULL, NULL); + clutter_effect_rotate (effect_template, actor->texture, + CLUTTER_Z_AXIS, 360.0, W / 2.0, H / 2.0, 0.0, + CLUTTER_ROTATE_CW, NULL, NULL); + clutter_effect_rotate (effect_template, actor->texture, + CLUTTER_X_AXIS, 360.0, 0.0, W / 4.0, 0.0, CLUTTER_ROTATE_CW, NULL, NULL); */ + + // Also change the actor's opacity while moving it along the path: + // (You would probably want to use a different ClutterEffectTemplate, + // so you could use a different alpha callback for this.) + //clutter_effect_fade (effect_template, actor->texture, 255, NULL, NULL); + + g_object_unref (animation); + //g_object_unref (timeline); + + return FALSE; +} + +static GstBusSyncReply +create_window (GstBus * bus, GstMessage * message, gpointer data) +{ + GstGLClutterActor *actor = (GstGLClutterActor *) data; + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + g_debug ("CREATING WINDOW"); + + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC + (message)), actor->win); + clutter_threads_add_idle ((GSourceFunc) create_actor, actor); + + gst_message_unref (message); + return GST_BUS_DROP; +} + +int +main (int argc, char *argv[]) +{ + GstPipeline *pipeline; + GstBus *bus; + ClutterActor *stage; + GstGLClutterActor *actor; + Display *disp; + Window stage_win; + ClutterInitError clutter_err = CLUTTER_INIT_ERROR_UNKNOWN; + + clutter_err = clutter_init (&argc, &argv); + if (clutter_err != CLUTTER_INIT_SUCCESS) + g_warning ("Failed to initalize clutter: %d\n", clutter_err); + + gst_init (&argc, &argv); + + disp = clutter_x11_get_default_display (); + if (!clutter_x11_has_composite_extension ()) { + g_error ("XComposite extension missing"); + } + + + stage = clutter_stage_get_default (); +// clutter_actor_set_size (CLUTTER_ACTOR (stage), W*3+2, H); + + stage_win = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + + actor = g_new0 (GstGLClutterActor, 1); + actor->stage = stage; + actor->win = XCreateSimpleWindow (disp, stage_win, 0, 0, W, H, 0, 0, 0); + XCompositeRedirectWindow (disp, actor->win, CompositeRedirectManual); + XMapRaised (disp, actor->win); + XSync (disp, FALSE); + + pipeline = + GST_PIPELINE (gst_parse_launch + ("videotestsrc ! video/x-raw, width=320, height=240, framerate=(fraction)30/1 ! " + "gleffects effect=twirl ! glimagesink", NULL)); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, actor, + NULL); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + + clutter_actor_show_all (stage); + + clutter_main (); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; +} diff --git a/tests/examples/gl/clutter/clutteractortee.c b/tests/examples/gl/clutter/clutteractortee.c new file mode 100644 index 0000000..a46f323 --- /dev/null +++ b/tests/examples/gl/clutter/clutteractortee.c @@ -0,0 +1,231 @@ +/* + * GStreamer + * Copyright (C) 2008 Filippo Argiolas + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GLIB_DISABLE_DEPRECATION_WARNINGS +#define CLUTTER_VERSION_MIN_REQUIRED CLUTTER_VERSION_1_8 + +#include +#include +#include +#include +#include +#include +#include + +#define ROWS 3 +#define COLS 3 +#define N_ACTORS ROWS*COLS +#define W 160 +#define H 120 + +struct GstGLClutterActor_ +{ + Window win; + Window root; + ClutterActor *texture; + ClutterActor *stage; +}; + +typedef struct GstGLClutterActor_ GstGLClutterActor; + +static gboolean +create_actor (GstGLClutterActor * actor) +{ + static gint xpos = 0; + static gint ypos = 0; + actor->texture = g_object_new (CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, + "window", actor->win, "automatic-updates", TRUE, NULL); + clutter_container_add_actor (CLUTTER_CONTAINER (actor->stage), + actor->texture); + clutter_actor_set_position (actor->texture, xpos, ypos); + + if (xpos > (COLS - 1) * W) { + xpos = 0; + ypos += H + 1; + } else + xpos += W + 1; + clutter_actor_show (actor->texture); + + return FALSE; +} + +static GstBusSyncReply +create_window (GstBus * bus, GstMessage * message, gpointer data) +{ + GstGLClutterActor **actor = (GstGLClutterActor **) data; + static gint count = 0; + static GMutex mutex; + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + g_mutex_lock (&mutex); + + if (count < N_ACTORS) { + g_message ("adding actor %d", count); + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC + (message)), actor[count]->win); + clutter_threads_add_idle ((GSourceFunc) create_actor, actor[count]); + count++; + } + + g_mutex_unlock (&mutex); + + gst_message_unref (message); + return GST_BUS_DROP; +} + +#if 0 +void +apply_fx (GstElement * element, const gchar * fx) +{ + GEnumClass *p_class; + + /* from fxtest ;) */ + /* heeeellppppp!! */ + p_class = + G_PARAM_SPEC_ENUM (g_object_class_find_property (G_OBJECT_GET_CLASS + (G_OBJECT (data)), "effect") + )->enum_class; + + g_print ("setting: %s - %s\n", fx, g_enum_get_value_by_nick (p_class, + fx)->value_name); + g_object_set (G_OBJECT (element), "effect", g_enum_get_value_by_nick (p_class, + fx)->value, NULL); +} +#endif + +int +main (int argc, char *argv[]) +{ + GstPipeline *pipeline; + GstBus *bus; + + GstElement *srcbin; + GstElement *tee; + GstElement *queue[N_ACTORS], *sink[N_ACTORS]; + GstElement *upload[N_ACTORS]; +/* + GstElement *effect[N_ACTORS]; +*/ + ClutterActor *stage; + GstGLClutterActor *actor[N_ACTORS]; + Display *disp; + Window stage_win; + const gchar *desc; + gint i; + gint ok = FALSE; + ClutterInitError clutter_err = CLUTTER_INIT_ERROR_UNKNOWN; + + clutter_err = clutter_init (&argc, &argv); + if (clutter_err != CLUTTER_INIT_SUCCESS) + g_warning ("Failed to initalize clutter: %d\n", clutter_err); + + gst_init (&argc, &argv); + + disp = clutter_x11_get_default_display (); + if (!clutter_x11_has_composite_extension ()) { + g_error ("XComposite extension missing"); + } + + stage = clutter_stage_get_default (); + clutter_actor_set_size (CLUTTER_ACTOR (stage), + W * COLS + (COLS - 1), H * ROWS + (ROWS - 1)); + + stage_win = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + XCompositeRedirectSubwindows (disp, stage_win, CompositeRedirectManual); + + for (i = 0; i < N_ACTORS; i++) { + actor[i] = g_new0 (GstGLClutterActor, 1); + actor[i]->stage = stage; + actor[i]->win = XCreateSimpleWindow (disp, stage_win, 0, 0, W, H, 0, 0, 0); + XMapRaised (disp, actor[i]->win); + XSync (disp, FALSE); + } +/* + desc = g_strdup_printf ("v4l2src ! " + "video/x-raw, width=640, height=480, framerate=30/1 ! " + "videoscale !" + "video/x-raw, width=%d, height=%d ! " + "identity", W, H); +*/ + desc = g_strdup_printf ("videotestsrc ! " + "video/x-raw, format=RGB, width=%d, height=%d !" "identity", W, H); + pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); + + srcbin = gst_parse_bin_from_description (desc, TRUE, NULL); + if (!srcbin) + g_error ("Source bin creation failed"); + + tee = gst_element_factory_make ("tee", NULL); + + gst_bin_add_many (GST_BIN (pipeline), srcbin, tee, NULL); + + for (i = 0; i < N_ACTORS; i++) { + queue[i] = gst_element_factory_make ("queue", NULL); + upload[i] = gst_element_factory_make ("glupload", NULL); +/* effect[i] = gst_element_factory_make ("gleffects", NULL); */ + sink[i] = gst_element_factory_make ("glimagesink", NULL); +/* gst_bin_add_many (GST_BIN (pipeline), + queue[i], upload[i], effect[i], sink[i], NULL); */ + gst_bin_add_many (GST_BIN (pipeline), queue[i], upload[i], sink[i], NULL); + } + + gst_element_link_many (srcbin, tee, NULL); + + for (i = 0; i < N_ACTORS; i++) { + ok |= +// gst_element_link_many (tee, queue[i], upload[i], effect[i], sink[i], + gst_element_link_many (tee, queue[i], upload[i], sink[i], NULL); + } + + if (!ok) + g_error ("Failed to link one or more elements"); + +/* + for (i = 0; i < N_ACTORS; i++) { + g_message ("setting effect %d on %s", i + 1, + gst_element_get_name (effect[i])); + g_object_set (G_OBJECT (effect[i]), "effect", i + 1, NULL); + } +*/ + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, actor, + NULL); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + + clutter_actor_show_all (stage); + + clutter_main (); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; +} diff --git a/tests/examples/gl/clutter/cluttershare.c b/tests/examples/gl/clutter/cluttershare.c new file mode 100644 index 0000000..d3f1b1e --- /dev/null +++ b/tests/examples/gl/clutter/cluttershare.c @@ -0,0 +1,383 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#define CLUTTER_VERSION_MIN_REQUIRED CLUTTER_VERSION_1_8 +#define CLUTTER_VERSION_MAX_ALLOWED CLUTTER_VERSION_1_10 +#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_16 +#define COGL_VERSION_MAX_ALLOWED COGL_VERSION_1_18 +#include +#ifndef WIN32 +#include +#include +#endif + +#include +#include +#include +#include + +/* This example shows how to use textures that come from a + * gst-plugins-gl pipeline, into the clutter framework + * It requires at least clutter 0.8.6 + */ + +/* rotation */ +static void +on_new_frame (ClutterTimeline * timeline, gint msecs, gpointer data) +{ + ClutterActor *rect_actor = CLUTTER_ACTOR (data); + ClutterActor *texture_actor = + g_object_get_data (G_OBJECT (timeline), "texture_actor"); + + clutter_actor_set_rotation (rect_actor, CLUTTER_Z_AXIS, + 60.0 * (gdouble) msecs / 1000.0, clutter_actor_get_width (rect_actor) / 2, + clutter_actor_get_height (rect_actor) / 2, 0); + + clutter_actor_set_rotation (texture_actor, CLUTTER_Z_AXIS, + 60.0 * (gdouble) msecs / 1000.0, + clutter_actor_get_width (texture_actor) / 6, + clutter_actor_get_height (texture_actor) / 6, 0); +} + + +/* clutter scene */ +static ClutterActor * +setup_stage (ClutterStage * stage) +{ + ClutterTimeline *timeline = NULL; + ClutterActor *texture_actor = NULL; + ClutterColor rect_color = { 125, 50, 200, 255 }; + ClutterActor *rect_actor = NULL; + + /* texture actor */ + + texture_actor = clutter_texture_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture_actor); + clutter_actor_set_position (texture_actor, 300, 170); + clutter_actor_set_scale (texture_actor, 0.6, 0.6); + clutter_actor_show (texture_actor); + g_object_set_data (G_OBJECT (texture_actor), "stage", stage); + + /* rectangle actor */ + + rect_actor = clutter_rectangle_new_with_color (&rect_color); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect_actor); + clutter_actor_set_size (rect_actor, 50, 50); + clutter_actor_set_position (rect_actor, 300, 300); + clutter_actor_show (rect_actor); + + /* timeline */ + + timeline = clutter_timeline_new (6000); + g_object_set_data (G_OBJECT (timeline), "texture_actor", texture_actor); + clutter_timeline_set_loop (timeline, TRUE); + clutter_timeline_start (timeline); + g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), + rect_actor); + + return texture_actor; +} + +/* put a gst gl buffer in the texture actor */ +static gboolean +update_texture_actor (gpointer data) +{ + ClutterTexture *texture_actor = (ClutterTexture *) data; + GAsyncQueue *queue_input_buf = + g_object_get_data (G_OBJECT (texture_actor), "queue_input_buf"); + GAsyncQueue *queue_output_buf = + g_object_get_data (G_OBJECT (texture_actor), "queue_output_buf"); + GstBuffer *inbuf = g_async_queue_pop (queue_input_buf); + ClutterActor *stage = g_object_get_data (G_OBJECT (texture_actor), "stage"); + CoglHandle cogl_texture = 0; + GstVideoMeta *v_meta; + GstVideoInfo info; + GstVideoFrame frame; + guint tex_id; + + v_meta = gst_buffer_get_video_meta (inbuf); + if (!v_meta) { + g_warning ("Required Meta was not found on buffers"); + return FALSE; + } + + gst_video_info_set_format (&info, v_meta->format, v_meta->width, + v_meta->height); + + if (!gst_video_frame_map (&frame, &info, inbuf, GST_MAP_READ | GST_MAP_GL)) { + g_warning ("Failed to map video frame"); + return FALSE; + } + + if (!gst_is_gl_memory (frame.map[0].memory)) { + g_warning ("Input buffer does not have GLMemory"); + gst_video_frame_unmap (&frame); + return FALSE; + } + + tex_id = *(guint *) frame.data[0]; + + /* Create a cogl texture from the gst gl texture */ + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, tex_id); + if (glGetError () != GL_NO_ERROR) + g_debug ("failed to bind texture that comes from gst-gl\n"); + cogl_texture = cogl_texture_new_from_foreign (tex_id, + GL_TEXTURE_2D, v_meta->width, v_meta->height, 0, 0, + COGL_PIXEL_FORMAT_RGBA_8888); + glBindTexture (GL_TEXTURE_2D, 0); + + gst_video_frame_unmap (&frame); + + /* Previous cogl texture is replaced and so its ref counter discreases to 0. + * According to the source code, glDeleteTexture is not called when the previous + * ref counter of the previous cogl texture is reaching 0 because is_foreign is TRUE */ + clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (texture_actor), + cogl_texture); + cogl_handle_unref (cogl_texture); + + /* we can now show the clutter scene if not yet visible */ + if (!CLUTTER_ACTOR_IS_VISIBLE (stage)) + clutter_actor_show_all (stage); + + /* push buffer so it can be unref later */ + g_async_queue_push (queue_output_buf, inbuf); + + return FALSE; +} + + +/* fakesink handoff callback */ +static void +on_gst_buffer (GstElement * element, GstBuffer * buf, GstPad * pad, + ClutterActor * texture_actor) +{ + GAsyncQueue *queue_input_buf = NULL; + GAsyncQueue *queue_output_buf = NULL; + + /* ref then push buffer to use it in clutter */ + gst_buffer_ref (buf); + queue_input_buf = + g_object_get_data (G_OBJECT (texture_actor), "queue_input_buf"); + g_async_queue_push (queue_input_buf, buf); + if (g_async_queue_length (queue_input_buf) > 2) + clutter_threads_add_idle_full (G_PRIORITY_HIGH, update_texture_actor, + texture_actor, NULL); + + /* pop then unref buffer we have finished to use in clutter */ + queue_output_buf = + g_object_get_data (G_OBJECT (texture_actor), "queue_output_buf"); + if (g_async_queue_length (queue_output_buf) > 2) { + GstBuffer *buf_old = g_async_queue_pop (queue_output_buf); + gst_buffer_unref (buf_old); + } +} + +/* gst bus signal watch callback */ +static void +end_stream_cb (GstBus * bus, GstMessage * msg, gpointer data) +{ + switch (GST_MESSAGE_TYPE (msg)) { + + case GST_MESSAGE_EOS: + g_print ("End-of-stream\n"); + g_print + ("For more information, try to run: GST_DEBUG=gldisplay:2 ./cluttershare\n"); + break; + + case GST_MESSAGE_ERROR: + { + 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 deails: %s\n", debug); + g_free (debug); + } + + break; + } + + default: + break; + } + + clutter_main_quit (); +} + +int +main (int argc, char *argv[]) +{ + ClutterInitError clutter_err = CLUTTER_INIT_ERROR_UNKNOWN; +#ifdef WIN32 + HGLRC clutter_gl_context = 0; + HDC clutter_dc = 0; +#else + Display *clutter_display = NULL; + Window clutter_win = 0; + GLXContext clutter_gl_context = NULL; +#endif + GstPipeline *pipeline = NULL; + GstBus *bus = NULL; + GstElement *glfilter = NULL; + GstState state = 0; + ClutterActor *stage = NULL; + ClutterActor *clutter_texture = NULL; + GAsyncQueue *queue_input_buf = NULL; + GAsyncQueue *queue_output_buf = NULL; + GstElement *fakesink = NULL; + + /* init gstreamer then clutter */ + + gst_init (&argc, &argv); + clutter_threads_init (); + clutter_err = clutter_init (&argc, &argv); + if (clutter_err != CLUTTER_INIT_SUCCESS) + g_warning ("Failed to initalize clutter: %d\n", clutter_err); + clutter_threads_enter (); + g_print ("clutter version: %s\n", CLUTTER_VERSION_S); + clutter_set_default_frame_rate (2); + + /* avoid to dispatch unecesary events */ + clutter_ungrab_keyboard (); + clutter_ungrab_pointer (); + + /* retrieve and turn off clutter opengl context */ + stage = clutter_stage_get_default (); + +#ifdef WIN32 + clutter_gl_context = wglGetCurrentContext (); + clutter_dc = wglGetCurrentDC (); + wglMakeCurrent (0, 0); +#else + clutter_display = clutter_x11_get_default_display (); + clutter_win = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + clutter_gl_context = glXGetCurrentContext (); + glXMakeCurrent (clutter_display, None, 0); +#endif + + /* setup gstreamer pipeline */ + + pipeline = + GST_PIPELINE (gst_parse_launch + ("videotestsrc ! video/x-raw, width=320, height=240, framerate=(fraction)30/1 ! " + "gleffects effect=5 ! glfiltercube ! fakesink sync=1", NULL)); + + /* setup bus */ + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message::error", G_CALLBACK (end_stream_cb), NULL); + g_signal_connect (bus, "message::warning", G_CALLBACK (end_stream_cb), NULL); + g_signal_connect (bus, "message::eos", G_CALLBACK (end_stream_cb), NULL); + gst_object_unref (bus); + + /* clutter_gl_context is an external OpenGL context with which gst-plugins-gl want to share textures */ + glfilter = gst_bin_get_by_name (GST_BIN (pipeline), "glfiltercube0"); + g_object_set (G_OBJECT (glfilter), "external-opengl-context", + clutter_gl_context, NULL); + gst_object_unref (glfilter); + + /* NULL to PAUSED state pipeline to make sure the gst opengl context is created and + * shared with the clutter one */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED); + state = GST_STATE_PAUSED; + if (gst_element_get_state (GST_ELEMENT (pipeline), &state, NULL, + GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS) { + g_debug ("failed to pause pipeline\n"); + return -1; + } + + /* turn on back clutter opengl context */ +#ifdef WIN32 + wglMakeCurrent (clutter_dc, clutter_gl_context); +#else + glXMakeCurrent (clutter_display, clutter_win, clutter_gl_context); +#endif + + /* clutter stage */ + clutter_actor_set_size (stage, 640, 480); + clutter_actor_set_position (stage, 0, 0); + clutter_stage_set_title (CLUTTER_STAGE (stage), "clutter and gst-plugins-gl"); + clutter_texture = setup_stage (CLUTTER_STAGE (stage)); + + /* append a gst-gl texture to this queue when you do not need it no more */ + queue_input_buf = g_async_queue_new (); + queue_output_buf = g_async_queue_new (); + g_object_set_data (G_OBJECT (clutter_texture), "queue_input_buf", + queue_input_buf); + g_object_set_data (G_OBJECT (clutter_texture), "queue_output_buf", + queue_output_buf); + + /* set a callback to retrieve the gst gl textures */ + fakesink = gst_bin_get_by_name (GST_BIN (pipeline), "fakesink0"); + g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL); + g_signal_connect (fakesink, "handoff", G_CALLBACK (on_gst_buffer), + clutter_texture); + gst_object_unref (fakesink); + + /* play gst */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + + /* main loop */ + clutter_main (); + + /* before to deinitialize the gst-gl-opengl context, + * no shared context (here the clutter one) must be current + */ +#ifdef WIN32 + wglMakeCurrent (0, 0); +#else + glXMakeCurrent (clutter_display, None, 0); +#endif + + clutter_threads_leave (); + + /* stop and clean up the pipeline */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + gst_object_unref (pipeline); + + /* make sure there is no pending gst gl buffer in the communication queues + * between clutter and gst-gl + */ + while (g_async_queue_length (queue_input_buf) > 0) { + GstBuffer *buf = g_async_queue_pop (queue_input_buf); + gst_buffer_unref (buf); + } + + while (g_async_queue_length (queue_output_buf) > 0) { + GstBuffer *buf = g_async_queue_pop (queue_output_buf); + gst_buffer_unref (buf); + } + + g_print ("END\n"); + + return 0; +} diff --git a/tests/examples/gl/clutter/cluttershare.cbp b/tests/examples/gl/clutter/cluttershare.cbp new file mode 100644 index 0000000..cb4907e --- /dev/null +++ b/tests/examples/gl/clutter/cluttershare.cbp @@ -0,0 +1,76 @@ + + + + + + diff --git a/tests/examples/gl/cocoa/Makefile.am b/tests/examples/gl/cocoa/Makefile.am new file mode 100755 index 0000000..716aa30 --- /dev/null +++ b/tests/examples/gl/cocoa/Makefile.am @@ -0,0 +1,4 @@ + +if HAVE_WINDOW_COCOA +SUBDIRS = videooverlay +endif diff --git a/tests/examples/gl/cocoa/README b/tests/examples/gl/cocoa/README new file mode 100644 index 0000000..74fe337 --- /dev/null +++ b/tests/examples/gl/cocoa/README @@ -0,0 +1,9 @@ +--- Description of the Cocoa examples --- + +- videooverlay: +Show how to use the videooverlay interface through Cocoa. +For now, the source is videotestsrc. (not yet a video file) + +--- How to build the Cocoa examples on GNUstep environnements --- + +make clean all -f GNUmakefile.gnustep diff --git a/tests/examples/gl/cocoa/videooverlay/.gitignore b/tests/examples/gl/cocoa/videooverlay/.gitignore new file mode 100644 index 0000000..c5ee310 --- /dev/null +++ b/tests/examples/gl/cocoa/videooverlay/.gitignore @@ -0,0 +1 @@ +videooverlay diff --git a/tests/examples/gl/cocoa/videooverlay/Makefile.am b/tests/examples/gl/cocoa/videooverlay/Makefile.am new file mode 100755 index 0000000..47dac61 --- /dev/null +++ b/tests/examples/gl/cocoa/videooverlay/Makefile.am @@ -0,0 +1,14 @@ +if HAVE_WINDOW_COCOA + +noinst_PROGRAMS = videooverlay + +videooverlay_SOURCES = main.m + +videooverlay_OBJCFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ + $(GL_CFLAGS) -I/usr/local/include/gstreamer-${GST_API_VERSION} ${GL_OBJCFLAGS} +videooverlay_LDADD=$(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) + +videooverlay_LIBTOOLFLAGS = --tag=OBJC + +endif diff --git a/tests/examples/gl/cocoa/videooverlay/main.m b/tests/examples/gl/cocoa/videooverlay/main.m new file mode 100755 index 0000000..8f959fe --- /dev/null +++ b/tests/examples/gl/cocoa/videooverlay/main.m @@ -0,0 +1,240 @@ +#include +#include +#include + +/* ============================================================= */ +/* */ +/* MainWindow */ +/* */ +/* ============================================================= */ + +@interface MainWindow: NSWindow { + GMainLoop *m_loop; + GstElement *m_pipeline; + gboolean m_isClosed; +} +- (id) initWithContentRect:(NSRect) contentRect Loop:(GMainLoop*)loop Pipeline:(GstElement*)pipeline; +- (GMainLoop*) loop; +- (GstElement*) pipeline; +- (gboolean) isClosed; +@end + +@implementation MainWindow + +- (id) initWithContentRect:(NSRect)contentRect Loop:(GMainLoop*)loop Pipeline:(GstElement*)pipeline +{ + m_loop = loop; + m_pipeline = pipeline; + m_isClosed = FALSE; + + self = [super initWithContentRect: contentRect + styleMask: (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask) + backing: NSBackingStoreBuffered defer: NO screen: nil]; + + [self setReleasedWhenClosed:NO]; + [NSApp setDelegate:self]; + + [self setTitle:@"gst-plugins-gl implements videooverlay interface"]; + + return self; +} + +- (GMainLoop*) loop { + return m_loop; +} + +- (GstElement*) pipeline { + return m_pipeline; +} + +- (gboolean) isClosed { + return m_isClosed; +} + +- (void) customClose { + m_isClosed = TRUE; +} + +- (BOOL) windowShouldClose:(id)sender { + gst_element_send_event (m_pipeline, gst_event_new_eos ()); + return YES; +} + +- (void) applicationDidFinishLaunching: (NSNotification *) not { + [self makeMainWindow]; + [self center]; + [self orderFront:self]; +} + +- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app { + return NO; +} + +@end + + +/* ============================================================= */ +/* */ +/* gstreamer callbacks */ +/* */ +/* ============================================================= */ + + +static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, MainWindow* window) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + g_print ("setting window handle %lud\n", (gulong) window); + + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), (guintptr) window); + + gst_message_unref (message); + + return GST_BUS_DROP; +} + + +static void end_stream_cb(GstBus* bus, GstMessage* message, MainWindow* window) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + g_print ("end of stream\n"); + + gst_element_set_state ([window pipeline], GST_STATE_NULL); + gst_object_unref ([window pipeline]); + g_main_loop_quit ([window loop]); + + [window performSelectorOnMainThread:@selector(customClose) withObject:nil waitUntilDone:YES]; + + [pool release]; +} + +static gpointer thread_func (MainWindow* window) +{ +#ifdef GNUSTEP + GSRegisterCurrentThread(); +#endif + + g_main_loop_run ([window loop]); + +#ifdef GNUSTEP + GSUnregisterCurrentThread(); +#endif + return NULL; +} + + +/* ============================================================= */ +/* */ +/* application */ +/* */ +/* ============================================================= */ + +int main(int argc, char **argv) +{ + int width = 640; + int height = 480; + + GMainLoop *loop = NULL; + GstElement *pipeline = NULL; + + GstElement *videosrc = NULL; + GstElement *videosink = NULL; + GstCaps *caps=NULL; + gboolean ok=FALSE; + GstBus *bus=NULL; + GThread *loop_thread=NULL; + NSAutoreleasePool *pool=nil; + NSRect rect; + MainWindow *window=nil; + +#ifdef GNUSTEP + GstState state; +#endif + + g_print("app created\n"); + + gst_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + pipeline = gst_pipeline_new ("pipeline"); + + videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc"); + videosink = gst_element_factory_make ("glimagesink", "glimagesink"); + + g_object_set(G_OBJECT(videosrc), "num-buffers", 500, NULL); + + gst_bin_add_many (GST_BIN (pipeline), videosrc, videosink, NULL); + + caps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "framerate", GST_TYPE_FRACTION, 25, 1, + "format", G_TYPE_STRING, "I420", + NULL); + + ok = gst_element_link_filtered(videosrc, videosink, caps); + gst_caps_unref(caps); + if (!ok) + g_warning("could not link videosrc to videosink\n"); + +#ifdef GNUSTEP + gst_element_set_state (pipeline, GST_STATE_PAUSED); + state = GST_STATE_PAUSED; + gst_element_get_state (pipeline, &state, &state, GST_CLOCK_TIME_NONE); + g_print("pipeline paused\n"); + GSRegisterCurrentThread(); +#endif + + pool = [[NSAutoreleasePool alloc] init]; +#ifndef GNUSTEP + [NSApplication sharedApplication]; +#endif + + rect.origin.x = 0; rect.origin.y = 0; + rect.size.width = width; rect.size.height = height; + + window = [[MainWindow alloc] initWithContentRect:rect Loop:loop Pipeline:pipeline]; + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), window); + g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), window); + g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), window); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, window, NULL); + gst_object_unref (bus); + + loop_thread = g_thread_new (NULL, + (GThreadFunc) thread_func, window); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + [window orderFront:window]; + +#ifndef GNUSTEP + while (![window isClosed]) { + NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate dateWithTimeIntervalSinceNow:1] + inMode:NSDefaultRunLoopMode dequeue:YES]; + if (event) + [NSApp sendEvent:event]; + } +#endif + + g_thread_join (loop_thread); + + [window release]; + + [pool release]; + +#ifdef GNUSTEP + GSUnregisterCurrentThread(); +#endif + + return 0; +} diff --git a/tests/examples/gl/generic/Makefile.am b/tests/examples/gl/generic/Makefile.am new file mode 100644 index 0000000..a334e12 --- /dev/null +++ b/tests/examples/gl/generic/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = cube cubeyuv doublecube recordgraphic diff --git a/tests/examples/gl/generic/README b/tests/examples/gl/generic/README new file mode 100644 index 0000000..a91d6bf --- /dev/null +++ b/tests/examples/gl/generic/README @@ -0,0 +1,21 @@ +--- Description of the generic (no GUI) examples --- + +- cube: +Show how to have a graphic FPS greater than the input video frame rate. +The source is the videotestsrc rgb. + +- cubeyuv: +Show how to have a graphic FPS greater than the input video frame rate. +The source is a local video file needed in argument. +The colorspace conversion is maded by the glupload element. + +- doublecube: +A local video source is displayed into two renderers. +The first one is a normal 2D screen, the second is a 3D cube. +We can visually check that the video is displayed at the same speed +in the two renderers. + +- recordgraphic: +Show how to use the glfilterapp to define the draw callback in a gstreamer client code. +The scene is recorded into an avi file using mpeg4 encoder. +The colorspace conversion is made by the gldownload element. diff --git a/tests/examples/gl/generic/cube/.gitignore b/tests/examples/gl/generic/cube/.gitignore new file mode 100644 index 0000000..3c088dd --- /dev/null +++ b/tests/examples/gl/generic/cube/.gitignore @@ -0,0 +1 @@ +cube diff --git a/tests/examples/gl/generic/cube/Makefile.am b/tests/examples/gl/generic/cube/Makefile.am new file mode 100644 index 0000000..41a5e2c --- /dev/null +++ b/tests/examples/gl/generic/cube/Makefile.am @@ -0,0 +1,8 @@ + +noinst_PROGRAMS = cube + +cube_SOURCES = main.cpp + +cube_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_CXXFLAGS) $(GL_CFLAGS) +cube_LDADD=$(GST_PLUGINS_GL_LIBS) $(GST_LIBS) $(GL_LIBS) + diff --git a/tests/examples/gl/generic/cube/cube.vcproj b/tests/examples/gl/generic/cube/cube.vcproj new file mode 100644 index 0000000..aefb6ad --- /dev/null +++ b/tests/examples/gl/generic/cube/cube.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/generic/cube/main.cpp b/tests/examples/gl/generic/cube/main.cpp new file mode 100644 index 0000000..b3b167c --- /dev/null +++ b/tests/examples/gl/generic/cube/main.cpp @@ -0,0 +1,248 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#if __WIN32__ || _WIN32 +# include +#endif +#include + +#include +#include + +static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_EOS: + g_print ("End-of-stream\n"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_ERROR: + { + 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 deails: %s\n", debug); + g_free (debug); + } + + g_main_loop_quit (loop); + break; + } + default: + break; + } + + return TRUE; +} + +//client reshape callback +static void reshapeCallback (GLuint width, GLuint height, gpointer data) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100); + glMatrixMode(GL_MODELVIEW); +} + +//client draw callback + +static gboolean drawCallback (GLuint texture, GLuint width, GLuint height, gpointer data) +{ + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + static GTimeVal current_time; + static glong last_sec = current_time.tv_sec; + static gint nbFrames = 0; + + g_get_current_time (¤t_time); + nbFrames++ ; + + if ((current_time.tv_sec - last_sec) >= 1) + { + std::cout << "GRPHIC FPS = " << nbFrames << std::endl; + nbFrames = 0; + last_sec = current_time.tv_sec; + } + + glEnable(GL_DEPTH_TEST); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(xrot,1.0f,0.0f,0.0f); + glRotatef(yrot,0.0f,1.0f,0.0f); + glRotatef(zrot,0.0f,0.0f,1.0f); + + glBegin(GL_QUADS); + // Front Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot+=0.3f; + yrot+=0.2f; + zrot+=0.4f; + + //return TRUE causes a postRedisplay + return TRUE; +} + + +//gst-launch-1.0 videotestsrc num_buffers=400 ! video/x-raw, width=320, height=240 ! +//glgraphicmaker ! glfiltercube ! video/x-raw, width=800, height=600 ! glimagesink +gint main (gint argc, gchar *argv[]) +{ + GstStateChangeReturn ret; + GstElement *pipeline, *videosrc, *glimagesink; + + GMainLoop *loop; + GstBus *bus; + + /* initialization */ + gst_init (&argc, &argv); + loop = g_main_loop_new (NULL, FALSE); + + /* create elements */ + pipeline = gst_pipeline_new ("pipeline"); + + /* watch for messages on the pipeline's bus (note that this will only + * work like this when a GLib main loop is running) */ + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, bus_call, loop); + gst_object_unref (bus); + + /* create elements */ + videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc0"); + glimagesink = gst_element_factory_make ("glimagesink", "glimagesink0"); + + + if (!videosrc || !glimagesink) + { + g_print ("one element could not be found \n"); + return -1; + } + + /* change video source caps */ + GstCaps *caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, "RGB", + "width", G_TYPE_INT, 320, + "height", G_TYPE_INT, 240, + "framerate", GST_TYPE_FRACTION, 25, 1, + NULL) ; + + /* configure elements */ + g_object_set(G_OBJECT(videosrc), "num-buffers", 400, NULL); + g_object_set(G_OBJECT(glimagesink), "client-reshape-callback", reshapeCallback, NULL); + g_object_set(G_OBJECT(glimagesink), "client-draw-callback", drawCallback, NULL); + g_object_set(G_OBJECT(glimagesink), "client-data", NULL, NULL); + + /* add elements */ + gst_bin_add_many (GST_BIN (pipeline), videosrc, glimagesink, NULL); + + /* link elements */ + gboolean link_ok = gst_element_link_filtered(videosrc, glimagesink, caps) ; + gst_caps_unref(caps) ; + if(!link_ok) + { + g_warning("Failed to link videosrc to glimagesink!\n") ; + return -1 ; + } + + /* run */ + ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + g_print ("Failed to start up pipeline!\n"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + + gst_message_parse_error (msg, &err, NULL); + g_print ("ERROR: %s\n", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return -1; + } + + g_main_loop_run (loop); + + /* clean up */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; + +} diff --git a/tests/examples/gl/generic/cubeyuv/.gitignore b/tests/examples/gl/generic/cubeyuv/.gitignore new file mode 100644 index 0000000..bd5c0ce --- /dev/null +++ b/tests/examples/gl/generic/cubeyuv/.gitignore @@ -0,0 +1 @@ +cubeyuv diff --git a/tests/examples/gl/generic/cubeyuv/Makefile.am b/tests/examples/gl/generic/cubeyuv/Makefile.am new file mode 100644 index 0000000..0fe4fcd --- /dev/null +++ b/tests/examples/gl/generic/cubeyuv/Makefile.am @@ -0,0 +1,8 @@ + +noinst_PROGRAMS = cubeyuv + +cubeyuv_SOURCES = main.cpp + +cubeyuv_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_CXXFLAGS) $(GL_CFLAGS) +cubeyuv_LDADD=$(GST_PLUGINS_GL_LIBS) $(GST_LIBS) $(GL_LIBS) + diff --git a/tests/examples/gl/generic/cubeyuv/cubeyuv.vcproj b/tests/examples/gl/generic/cubeyuv/cubeyuv.vcproj new file mode 100644 index 0000000..1d20859 --- /dev/null +++ b/tests/examples/gl/generic/cubeyuv/cubeyuv.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/generic/cubeyuv/main.cpp b/tests/examples/gl/generic/cubeyuv/main.cpp new file mode 100644 index 0000000..6e17b6a --- /dev/null +++ b/tests/examples/gl/generic/cubeyuv/main.cpp @@ -0,0 +1,325 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#if __WIN32__ || _WIN32 +# include +#endif +#include + +#include +#include +#include + +static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_EOS: + g_print ("End-of-stream\n"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_ERROR: + { + 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 deails: %s\n", debug); + g_free (debug); + } + + g_main_loop_quit (loop); + break; + } + default: + break; + } + + return TRUE; +} + + +//display video framerate +static void identityCallback (GstElement *src, GstBuffer *buffer, GstElement* textoverlay) +{ + static GstClockTime last_timestamp = 0; + static gint nbFrames = 0 ; + + //display estimated video FPS + nbFrames++ ; + if (GST_BUFFER_TIMESTAMP(buffer) - last_timestamp >= 1000000000) + { + std::ostringstream oss ; + oss << "video framerate = " << nbFrames ; + std::string s(oss.str()) ; + g_object_set(G_OBJECT(textoverlay), "text", s.c_str(), NULL); + last_timestamp = GST_BUFFER_TIMESTAMP(buffer) ; + nbFrames = 0 ; + } +} + + +//client reshape callback +static void reshapeCallback (GLuint width, GLuint height) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100); + glMatrixMode(GL_MODELVIEW); +} + + +//client draw callback +static gboolean drawCallback (GLuint texture, GLuint width, GLuint height) +{ + + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + static GTimeVal current_time; + static glong last_sec = current_time.tv_sec; + static gint nbFrames = 0; + + g_get_current_time (¤t_time); + nbFrames++ ; + + if ((current_time.tv_sec - last_sec) >= 1) + { + std::cout << "GRPHIC FPS = " << nbFrames << std::endl; + nbFrames = 0; + last_sec = current_time.tv_sec; + } + + glEnable(GL_DEPTH_TEST); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(xrot,1.0f,0.0f,0.0f); + glRotatef(yrot,0.0f,1.0f,0.0f); + glRotatef(zrot,0.0f,0.0f,1.0f); + + glBegin(GL_QUADS); + // Front Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot+=0.03f; + yrot+=0.02f; + zrot+=0.04f; + + //return TRUE causes a postRedisplay + //so you have to return FALSE to synchronise to have a graphic FPS + //equals to the input video frame rate + + //Usually, we will not always return TRUE (or FALSE) + //For example, if you want a fixed graphic FPS equals to 60 + //then you have to use the timeclock to return TRUE or FALSE + //in order to increase or decrease the FPS in real time + //to reach the 60. + + return TRUE; +} + + +static void cb_new_pad (GstElement* decodebin, GstPad* pad, GstElement* identity) +{ + GstPad* identity_pad = gst_element_get_static_pad (identity, "sink"); + + //only link once + if (GST_PAD_IS_LINKED (identity_pad)) + { + gst_object_unref (identity_pad); + return; + } + + GstCaps* caps = gst_pad_get_current_caps (pad); + GstStructure* str = gst_caps_get_structure (caps, 0); + if (!g_strrstr (gst_structure_get_name (str), "video")) + { + gst_caps_unref (caps); + gst_object_unref (identity_pad); + return; + } + gst_caps_unref (caps); + + GstPadLinkReturn ret = gst_pad_link (pad, identity_pad); + if (ret != GST_PAD_LINK_OK) + g_warning ("Failed to link with decodebin!\n"); +} + + +gint main (gint argc, gchar *argv[]) +{ + if (argc != 2) + { + g_warning ("usage: cubeyuv.exe videolocation\n"); + return -1; + } + + std::string video_location(argv[1]); + + /* initialization */ + gst_init (&argc, &argv); + GMainLoop* loop = g_main_loop_new (NULL, FALSE); + + /* create elements */ + GstElement* pipeline = gst_pipeline_new ("pipeline"); + + /* watch for messages on the pipeline's bus (note that this will only + * work like this when a GLib main loop is running) */ + GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, bus_call, loop); + gst_object_unref (bus); + + /* create elements */ + GstElement* videosrc = gst_element_factory_make ("filesrc", "filesrc0"); + GstElement* decodebin = gst_element_factory_make ("decodebin", "decodebin"); + GstElement* identity = gst_element_factory_make ("identity", "identity0"); + GstElement* textoverlay = gst_element_factory_make ("textoverlay", "textoverlay0"); + GstElement* glcolorscale = gst_element_factory_make ("glcolorscale", "glcolorscale0"); + GstElement* glimagesink = gst_element_factory_make ("glimagesink", "glimagesink0"); + + + if (!videosrc || !decodebin || !identity || !textoverlay || + !glcolorscale || !glimagesink) + { + g_print ("one element could not be found \n"); + return -1; + } + + GstCaps *outcaps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 640, + "height", G_TYPE_INT, 480, + NULL); + + /* configure elements */ + g_object_set(G_OBJECT(videosrc), "num-buffers", 800, NULL); + g_object_set(G_OBJECT(videosrc), "location", video_location.c_str(), NULL); + g_signal_connect(identity, "handoff", G_CALLBACK(identityCallback), textoverlay) ; + g_object_set(G_OBJECT(textoverlay), "font_desc", "Ahafoni CLM Bold 30", NULL); + g_object_set(G_OBJECT(glimagesink), "client-reshape-callback", reshapeCallback, NULL); + g_object_set(G_OBJECT(glimagesink), "client-draw-callback", drawCallback, NULL); + + /* add elements */ + gst_bin_add_many (GST_BIN (pipeline), videosrc, decodebin, identity, + textoverlay, glcolorscale, glimagesink, NULL); + + /* link elements */ + gst_element_link_pads (videosrc, "src", decodebin, "sink"); + + g_signal_connect (decodebin, "pad-added", G_CALLBACK (cb_new_pad), identity); + + if (!gst_element_link_pads(identity, "src", textoverlay, "video_sink")) + { + g_print ("Failed to link identity to textoverlay!\n"); + return -1; + } + + gst_element_link (textoverlay, glcolorscale); + + gboolean link_ok = gst_element_link_filtered(glcolorscale, glimagesink, outcaps) ; + gst_caps_unref(outcaps) ; + if(!link_ok) + { + g_warning("Failed to link textoverlay to glimagesink!\n") ; + return -1 ; + } + + /* run */ + GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + g_print ("Failed to start up pipeline!\n"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + + gst_message_parse_error (msg, &err, NULL); + g_print ("ERROR: %s\n", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return -1; + } + + g_main_loop_run (loop); + + /* clean up */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; + +} + diff --git a/tests/examples/gl/generic/doublecube/.gitignore b/tests/examples/gl/generic/doublecube/.gitignore new file mode 100644 index 0000000..e9f4f13 --- /dev/null +++ b/tests/examples/gl/generic/doublecube/.gitignore @@ -0,0 +1 @@ +doublecube diff --git a/tests/examples/gl/generic/doublecube/Makefile.am b/tests/examples/gl/generic/doublecube/Makefile.am new file mode 100644 index 0000000..7a9184b --- /dev/null +++ b/tests/examples/gl/generic/doublecube/Makefile.am @@ -0,0 +1,8 @@ + +noinst_PROGRAMS = doublecube + +doublecube_SOURCES = main.cpp + +doublecube_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_CXXFLAGS) $(GL_CFLAGS) +doublecube_LDADD=$(GST_PLUGINS_GL_LIBS) $(GST_LIBS) $(GL_LIBS) + diff --git a/tests/examples/gl/generic/doublecube/doublecube.vcproj b/tests/examples/gl/generic/doublecube/doublecube.vcproj new file mode 100644 index 0000000..db0364b --- /dev/null +++ b/tests/examples/gl/generic/doublecube/doublecube.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/generic/doublecube/main.cpp b/tests/examples/gl/generic/doublecube/main.cpp new file mode 100644 index 0000000..4f28336 --- /dev/null +++ b/tests/examples/gl/generic/doublecube/main.cpp @@ -0,0 +1,368 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#if __WIN32__ || _WIN32 +# include +#endif +#include + +#include +#include +#include + +static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_EOS: + g_print ("End-of-stream\n"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_ERROR: + { + 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 (loop); + break; + } + default: + break; + } + + return TRUE; +} + + +//display video framerate +static GstPadProbeReturn textoverlay_sink_pad_probe_cb (GstPad *pad, GstPadProbeInfo *info, GstElement* textoverlay) +{ + static GstClockTime last_timestamp = 0; + static gint nbFrames = 0 ; + + //display estimated video FPS + nbFrames++ ; + if (GST_BUFFER_TIMESTAMP(info->data) - last_timestamp >= 1000000000) + { + std::ostringstream oss ; + oss << "video framerate = " << nbFrames ; + std::string s(oss.str()) ; + g_object_set(G_OBJECT(textoverlay), "text", s.c_str(), NULL); + last_timestamp = GST_BUFFER_TIMESTAMP(info->data) ; + nbFrames = 0 ; + } + + return GST_PAD_PROBE_OK; +} + + +//client reshape callback +static void reshapeCallback (GLuint width, GLuint height) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100); + glMatrixMode(GL_MODELVIEW); +} + + +//client draw callback +static gboolean drawCallback (GLuint texture, GLuint width, GLuint height) +{ + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + static GTimeVal current_time; + static glong last_sec = current_time.tv_sec; + static gint nbFrames = 0; + + g_get_current_time (¤t_time); + nbFrames++ ; + + if ((current_time.tv_sec - last_sec) >= 1) + { + std::cout << "GRAPHIC FPS of the scene which contains the custom cube) = " << nbFrames << std::endl; + nbFrames = 0; + last_sec = current_time.tv_sec; + } + + glEnable(GL_DEPTH_TEST); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(xrot,1.0f,0.0f,0.0f); + glRotatef(yrot,0.0f,1.0f,0.0f); + glRotatef(zrot,0.0f,0.0f,1.0f); + + glBegin(GL_QUADS); + // Front Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot+=0.03f; + yrot+=0.02f; + zrot+=0.04f; + + //return TRUE causes a postRedisplay + //so you have to return FALSE to synchronise to have a graphic FPS + //equals to the input video frame rate + + //Usually, we will not always return TRUE (or FALSE) + //For example, if you want a fixed graphic FPS equals to 60 + //then you have to use the timeclock to return TRUE or FALSE + //in order to increase or decrease the FPS in real time + //to reach the 60. + + return TRUE; +} + + +static void cb_new_pad (GstElement* decodebin, GstPad* pad, GstElement* element) +{ + GstPad* element_pad = gst_element_get_static_pad (element, "sink"); + + //only link once + if (!element_pad || GST_PAD_IS_LINKED (element_pad)) + { + gst_object_unref (element_pad); + return; + } + + GstCaps* caps = gst_pad_get_current_caps (pad); + GstStructure* str = gst_caps_get_structure (caps, 0); + + GstCaps* caps2 = gst_pad_query_caps (element_pad, NULL); + gst_caps_unref (caps2); + + if (!g_strrstr (gst_structure_get_name (str), "video")) + { + gst_caps_unref (caps); + gst_object_unref (element_pad); + return; + } + gst_caps_unref (caps); + + GstPadLinkReturn ret = gst_pad_link (pad, element_pad); + if (ret != GST_PAD_LINK_OK) + g_warning ("Failed to link with decodebin %d!\n", ret); + gst_object_unref (element_pad); +} + + +gint main (gint argc, gchar *argv[]) +{ + + if (argc != 2) + { + g_warning ("usage: doublecube.exe videolocation\n"); + return -1; + } + + std::string video_location(argv[1]); + + /* initialization */ + gst_init (&argc, &argv); + GMainLoop* loop = g_main_loop_new (NULL, FALSE); + + /* create elements */ + GstElement* pipeline = gst_pipeline_new ("pipeline"); + + /* watch for messages on the pipeline's bus (note that this will only + * work like this when a GLib main loop is running) */ + GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, bus_call, loop); + gst_object_unref (bus); + + /* create elements */ + GstElement* videosrc = gst_element_factory_make ("filesrc", "filesrc0"); + GstElement* decodebin = gst_element_factory_make ("decodebin", "decodebin0"); + GstElement* videoconvert = gst_element_factory_make ("videoscale", "videoconvert0"); + GstElement* textoverlay = gst_element_factory_make ("textoverlay", "textoverlay0"); //textoverlay required I420 + GstElement* tee = gst_element_factory_make ("tee", "tee0"); + + GstElement* queue0 = gst_element_factory_make ("queue", "queue0"); + GstElement* glimagesink0 = gst_element_factory_make ("glimagesink", "glimagesink0"); + + GstElement* queue1 = gst_element_factory_make ("queue", "queue1"); + GstElement* glfiltercube = gst_element_factory_make ("glfiltercube", "glfiltercube"); + GstElement* glimagesink1 = gst_element_factory_make ("glimagesink", "glimagesink1"); + + GstElement* queue2 = gst_element_factory_make ("queue", "queue2"); + GstElement* glimagesink2 = gst_element_factory_make ("glimagesink", "glimagesink2"); + + + if (!videosrc || !decodebin || !videoconvert || !textoverlay || !tee || + !queue0 || !glimagesink0 || + !queue1 || !glfiltercube || !glimagesink1 || + !queue2 || !glimagesink2) + { + g_warning ("one element could not be found \n"); + return -1; + } + + GstCaps* cubecaps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 600, + "height", G_TYPE_INT, 400, + NULL); + + /* configure elements */ + g_object_set(G_OBJECT(videosrc), "num-buffers", 1000, NULL); + g_object_set(G_OBJECT(videosrc), "location", video_location.c_str(), NULL); + g_object_set(G_OBJECT(textoverlay), "font_desc", "Ahafoni CLM Bold 30", NULL); + g_object_set(G_OBJECT(glimagesink0), "client-reshape-callback", reshapeCallback, NULL); + g_object_set(G_OBJECT(glimagesink0), "client-draw-callback", drawCallback, NULL); + + /* add elements */ + gst_bin_add_many (GST_BIN (pipeline), videosrc, decodebin, videoconvert, textoverlay, tee, + queue0, glimagesink0, + queue1, glfiltercube, glimagesink1, + queue2, glimagesink2, NULL); + + GstPad* textoverlay_sink_pad = gst_element_get_static_pad (textoverlay, "video_sink"); + gst_pad_add_probe (textoverlay_sink_pad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) textoverlay_sink_pad_probe_cb, (gpointer)textoverlay, NULL); + gst_object_unref (textoverlay_sink_pad); + + if (!gst_element_link_many(videoconvert, textoverlay, tee, NULL)) + { + g_print ("Failed to link videoconvert to tee!\n"); + return -1; + } + + if (!gst_element_link(videosrc, decodebin)) + { + g_print ("Failed to link videosrc to decodebin!\n"); + return -1; + } + + g_signal_connect (decodebin, "pad-added", G_CALLBACK (cb_new_pad), videoconvert); + + if (!gst_element_link_many(tee, queue0, NULL)) + { + g_warning ("Failed to link one or more elements bettween tee and queue0!\n"); + return -1; + } + + gboolean link_ok = gst_element_link_filtered(queue0, glimagesink0, cubecaps) ; + gst_caps_unref(cubecaps) ; + if(!link_ok) + { + g_warning("Failed to link queue0 to glimagesink0!\n") ; + return -1 ; + } + + if (!gst_element_link_many(tee, queue1, glfiltercube, glimagesink1, NULL)) + { + g_warning ("Failed to link one or more elements bettween tee and glimagesink1!\n"); + return -1; + } + + if (!gst_element_link_many(tee, queue2, glimagesink2, NULL)) + { + g_warning ("Failed to link one or more elements bettween tee and glimagesink2!\n"); + return -1; + } + + /* run */ + GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + g_print ("Failed to start up pipeline!\n"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + + gst_message_parse_error (msg, &err, NULL); + g_print ("ERROR: %s\n", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return -1; + } + + g_main_loop_run (loop); + + /* clean up */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; + +} + diff --git a/tests/examples/gl/generic/generic.sln b/tests/examples/gl/generic/generic.sln new file mode 100644 index 0000000..22e86f6 --- /dev/null +++ b/tests/examples/gl/generic/generic.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cube", "cube\cube.vcproj", "{DA41FBFF-E1DE-4DA1-BB96-C56C63D5CDBE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeyuv", "cubeyuv\cubeyuv.vcproj", "{6C94B86A-8E34-4163-840A-BFD5C80B1F2E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "doublecube", "doublecube\doublecube.vcproj", "{4EC968E0-5B6C-418A-8A75-F390D56DFFE8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recordgraphic", "recordgraphic\recordgraphic.vcproj", "{E9A5E91B-5F8B-4322-9531-00CCFCB29A2D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DA41FBFF-E1DE-4DA1-BB96-C56C63D5CDBE}.Debug|Win32.ActiveCfg = Debug|Win32 + {DA41FBFF-E1DE-4DA1-BB96-C56C63D5CDBE}.Debug|Win32.Build.0 = Debug|Win32 + {DA41FBFF-E1DE-4DA1-BB96-C56C63D5CDBE}.Release|Win32.ActiveCfg = Release|Win32 + {DA41FBFF-E1DE-4DA1-BB96-C56C63D5CDBE}.Release|Win32.Build.0 = Release|Win32 + {6C94B86A-8E34-4163-840A-BFD5C80B1F2E}.Debug|Win32.ActiveCfg = Debug|Win32 + {6C94B86A-8E34-4163-840A-BFD5C80B1F2E}.Debug|Win32.Build.0 = Debug|Win32 + {6C94B86A-8E34-4163-840A-BFD5C80B1F2E}.Release|Win32.ActiveCfg = Release|Win32 + {6C94B86A-8E34-4163-840A-BFD5C80B1F2E}.Release|Win32.Build.0 = Release|Win32 + {4EC968E0-5B6C-418A-8A75-F390D56DFFE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {4EC968E0-5B6C-418A-8A75-F390D56DFFE8}.Debug|Win32.Build.0 = Debug|Win32 + {4EC968E0-5B6C-418A-8A75-F390D56DFFE8}.Release|Win32.ActiveCfg = Release|Win32 + {4EC968E0-5B6C-418A-8A75-F390D56DFFE8}.Release|Win32.Build.0 = Release|Win32 + {E9A5E91B-5F8B-4322-9531-00CCFCB29A2D}.Debug|Win32.ActiveCfg = Debug|Win32 + {E9A5E91B-5F8B-4322-9531-00CCFCB29A2D}.Debug|Win32.Build.0 = Debug|Win32 + {E9A5E91B-5F8B-4322-9531-00CCFCB29A2D}.Release|Win32.ActiveCfg = Release|Win32 + {E9A5E91B-5F8B-4322-9531-00CCFCB29A2D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tests/examples/gl/generic/recordgraphic/.gitignore b/tests/examples/gl/generic/recordgraphic/.gitignore new file mode 100644 index 0000000..8127d83 --- /dev/null +++ b/tests/examples/gl/generic/recordgraphic/.gitignore @@ -0,0 +1 @@ +recordgraphic diff --git a/tests/examples/gl/generic/recordgraphic/Makefile.am b/tests/examples/gl/generic/recordgraphic/Makefile.am new file mode 100644 index 0000000..4ee21ac --- /dev/null +++ b/tests/examples/gl/generic/recordgraphic/Makefile.am @@ -0,0 +1,8 @@ + +noinst_PROGRAMS = recordgraphic + +recordgraphic_SOURCES = main.cpp + +recordgraphic_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CXXFLAGS) $(GL_CFLAGS) +recordgraphic_LDADD=$(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ $(GST_LIBS) $(GL_LIBS) + diff --git a/tests/examples/gl/generic/recordgraphic/main.cpp b/tests/examples/gl/generic/recordgraphic/main.cpp new file mode 100644 index 0000000..fd0ceb0 --- /dev/null +++ b/tests/examples/gl/generic/recordgraphic/main.cpp @@ -0,0 +1,278 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#if __WIN32__ || _WIN32 +# include +#endif +#include +#include + +#include +#include + +static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_EOS: + g_print ("End-of-stream\n"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_ERROR: + { + 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 (loop); + break; + } + default: + break; + } + + return TRUE; +} + +//client reshape callback +static void reshapeCallback (GLuint width, GLuint height) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100); + glMatrixMode(GL_MODELVIEW); +} + + +//client draw callback +static gboolean drawCallback (GLuint width, GLuint height, GLuint texture, gpointer data) +{ + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + static GTimeVal current_time; + static glong last_sec = current_time.tv_sec; + static gint nbFrames = 0; + + g_get_current_time (¤t_time); + nbFrames++ ; + + if ((current_time.tv_sec - last_sec) >= 1) + { + std::cout << "GRPHIC FPS = " << nbFrames << std::endl; + nbFrames = 0; + last_sec = current_time.tv_sec; + } + + glEnable(GL_DEPTH_TEST); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(xrot,1.0f,0.0f,0.0f); + glRotatef(yrot,0.0f,1.0f,0.0f); + glRotatef(zrot,0.0f,0.0f,1.0f); + + //cube + glBegin(GL_QUADS); + // Front Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot+=0.3f; + yrot+=0.2f; + zrot+=0.4f; + + //return TRUE causes a postRedisplay + return FALSE; +} + + +//equivalent command line: +//gst-launch-1.0 videotestsrc num_buffers=400 ! gleffects effect=0 ! +//avenc_mpeg4 ! avimux ! filesink location="record.avi" +// or +//gst-launch-1.0 videotestsrc num_buffers=400 ! gleffects effect=0 ! "video/x-raw, width=320, height=240" ! glfiltercube ! "video/x-raw, width=720, height=576" ! +//avenc_mpeg4 ! avimux ! filesink location="record.avi" +gint main (gint argc, gchar *argv[]) +{ + GstStateChangeReturn ret; + GstElement *pipeline, *videosrc, *glfilterapp, *avenc_mpeg4, *avimux, *filesink; + GMainLoop *loop; + GstBus *bus; + + /* initialization */ + gst_init (&argc, &argv); + loop = g_main_loop_new (NULL, FALSE); + + /* create elements */ + pipeline = gst_pipeline_new ("pipeline"); + + /* watch for messages on the pipeline's bus (note that this will only + * work like this when a GLib main loop is running) */ + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_watch (bus, bus_call, loop); + gst_object_unref (bus); + + /* create elements */ + videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc0"); + glfilterapp = gst_element_factory_make ("glfilterapp", "glfilterapp0"); + avenc_mpeg4 = gst_element_factory_make ("avenc_mpeg4", "avenc_mpeg40"); + avimux = gst_element_factory_make ("avimux", "avimux0"); + filesink = gst_element_factory_make ("filesink", "filesink0"); + + + if (!videosrc || !glfilterapp || !avenc_mpeg4 || !avimux || !filesink) + { + g_print ("one element could not be found \n"); + return -1; + } + + /* change video source caps */ + GstCaps *caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, "UYVY", + "width", G_TYPE_INT, 320, + "height", G_TYPE_INT, 240, + "framerate", GST_TYPE_FRACTION, 25, 1, + NULL); + + /* change video source caps */ + GstCaps *outcaps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 640, + "height", G_TYPE_INT, 480, + NULL); + + /* configure elements */ + g_object_set(G_OBJECT(videosrc), "num-buffers", 400, NULL); + g_object_set(G_OBJECT(glfilterapp), "client-reshape-callback", reshapeCallback, NULL); + g_object_set(G_OBJECT(glfilterapp), "client-draw-callback", drawCallback, NULL); + g_object_set(G_OBJECT(glfilterapp), "client-data", NULL, NULL); + g_object_set(G_OBJECT(filesink), "location", "record.avi", NULL); + + /* add elements */ + gst_bin_add_many (GST_BIN (pipeline), videosrc, glfilterapp, + avenc_mpeg4, avimux, filesink, NULL); + + /* link elements */ + gboolean link_ok = gst_element_link_filtered(videosrc, glfilterapp, caps) ; + gst_caps_unref(caps) ; + if(!link_ok) + { + g_warning("Failed to link videosrc to glfilterapp!\n") ; + return -1 ; + } + + link_ok = gst_element_link_filtered(glfilterapp, avenc_mpeg4, outcaps) ; + gst_caps_unref(outcaps) ; + if(!link_ok) + { + g_warning("Failed to link glfilterapp to avenc_mpeg4!\n") ; + return -1 ; + } + if (!gst_element_link_many(avenc_mpeg4, avimux, filesink, NULL)) + { + g_print ("Failed to link one or more elements!\n"); + return -1; + } + + + /* run */ + ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + g_print ("Failed to start up pipeline!\n"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + + gst_message_parse_error (msg, &err, NULL); + g_print ("ERROR: %s\n", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return -1; + } + + g_main_loop_run (loop); + + /* clean up */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; + +} diff --git a/tests/examples/gl/generic/recordgraphic/recordgraphic.vcproj b/tests/examples/gl/generic/recordgraphic/recordgraphic.vcproj new file mode 100644 index 0000000..66df375 --- /dev/null +++ b/tests/examples/gl/generic/recordgraphic/recordgraphic.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/Makefile.am b/tests/examples/gl/gtk/Makefile.am new file mode 100644 index 0000000..c64cd4a --- /dev/null +++ b/tests/examples/gl/gtk/Makefile.am @@ -0,0 +1,12 @@ + +SUBDIRS = . gtkvideooverlay filternovideooverlay filtervideooverlay fxtest switchvideooverlay + +noinst_LTLIBRARIES = libgstgtkhelper.la + +libgstgtkhelper_la_SOURCES = gstgtk.c gstgtk.h +libgstgtkhelper_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(GTK3_CFLAGS) + +if HAVE_WINDOW_COCOA +libgstgtkhelper_la_CFLAGS += -x objective-c +endif + diff --git a/tests/examples/gl/gtk/README b/tests/examples/gl/gtk/README new file mode 100644 index 0000000..76595c6 --- /dev/null +++ b/tests/examples/gl/gtk/README @@ -0,0 +1,27 @@ +--- Description of the GTK examples --- + +- gtkvideooverlay: +Show how to use the videooverlay interface through GTK. +It's possible to switch bettween GST_STATE through four buttons. + +-filternovideooverlay: +A more complex pipeline is switched bettween the GST states +without using the videooverlay interface. + +-filtervideooverlay: +A more complex pipeline is switched bettween the GST states. +using the videooverlay interface. + +-fxtest: +switch bettween effects of the gleffects filter. + +-pixbufdrop: +drag and drop a png file and overlay it using alpha channel. +It uses gloverlay filter. + +-switchvideooverlay: +change the videooverlay window while the stream is playing. + +--- How to build the GTK examples --- + +Using autotools or using tests/examples/gtk/gtk.sln diff --git a/tests/examples/gl/gtk/filternovideooverlay/.gitignore b/tests/examples/gl/gtk/filternovideooverlay/.gitignore new file mode 100644 index 0000000..f6e4162 --- /dev/null +++ b/tests/examples/gl/gtk/filternovideooverlay/.gitignore @@ -0,0 +1 @@ +filternovideooverlay diff --git a/tests/examples/gl/gtk/filternovideooverlay/Makefile.am b/tests/examples/gl/gtk/filternovideooverlay/Makefile.am new file mode 100644 index 0000000..61526c5 --- /dev/null +++ b/tests/examples/gl/gtk/filternovideooverlay/Makefile.am @@ -0,0 +1,18 @@ +noinst_PROGRAMS = filternovideooverlay + +filternovideooverlay_SOURCES = main.cpp + +filternovideooverlay_CXXFLAGS= \ + -I$(top_srcdir)/gst-libs \ + -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CXXFLAGS) \ + $(GL_CFLAGS) \ + $(GTK3_CFLAGS) + +filternovideooverlay_LDADD= \ + $(GTK3_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) \ + $(GST_LIBS) \ + $(GL_LIBS) + diff --git a/tests/examples/gl/gtk/filternovideooverlay/filternovideooverlay.vcproj b/tests/examples/gl/gtk/filternovideooverlay/filternovideooverlay.vcproj new file mode 100644 index 0000000..473587b --- /dev/null +++ b/tests/examples/gl/gtk/filternovideooverlay/filternovideooverlay.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/filternovideooverlay/main.cpp b/tests/examples/gl/gtk/filternovideooverlay/main.cpp new file mode 100644 index 0000000..5b9257c --- /dev/null +++ b/tests/examples/gl/gtk/filternovideooverlay/main.cpp @@ -0,0 +1,180 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * Copyright (C) 2014 Matthew Waters + * + * 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. + */ + +#include +#include +#include + + +static void end_stream_cb(GstBus* bus, GstMessage* message, GstElement* pipeline) +{ + g_print("End of stream\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + +static void button_state_null_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_NULL); + g_print ("GST_STATE_NULL\n"); +} + + +static void button_state_ready_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_READY); + g_print ("GST_STATE_READY\n"); +} + + +static void button_state_paused_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); + g_print ("GST_STATE_PAUSED\n"); +} + + +static void button_state_playing_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_print ("GST_STATE_PLAYING\n"); +} + + +static gchar* slider_fps_cb (GtkScale* scale, gdouble value, GstElement* pipeline) +{ + //change the video frame rate dynamically + return g_strdup_printf ("video framerate: %0.*g", gtk_scale_get_digits (scale), value); +} + + + +gint main (gint argc, gchar *argv[]) +{ + gtk_init (&argc, &argv); + gst_init (&argc, &argv); + + GstElement* pipeline = gst_pipeline_new ("pipeline"); + + //window to control the states + GtkWidget* window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL); + GdkGeometry geometry; + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window_control), window_control, &geometry, GDK_HINT_MIN_SIZE); + gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE); + gtk_window_move (GTK_WINDOW (window_control), 10, 10); + GtkWidget* table = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (window_control), table); + + //control state null + GtkWidget* button_state_null = gtk_button_new_with_label ("GST_STATE_NULL"); + g_signal_connect (G_OBJECT (button_state_null), "clicked", + G_CALLBACK (button_state_null_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_null, 0, 0, 1, 1); + gtk_widget_show (button_state_null); + + //control state ready + GtkWidget* button_state_ready = gtk_button_new_with_label ("GST_STATE_READY"); + g_signal_connect (G_OBJECT (button_state_ready), "clicked", + G_CALLBACK (button_state_ready_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_ready, 0, 1, 1, 1); + gtk_widget_show (button_state_ready); + + //control state paused + GtkWidget* button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED"); + g_signal_connect (G_OBJECT (button_state_paused), "clicked", + G_CALLBACK (button_state_paused_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_paused, 0, 2, 1, 1); + gtk_widget_show (button_state_paused); + + //control state playing + GtkWidget* button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING"); + g_signal_connect (G_OBJECT (button_state_playing), "clicked", + G_CALLBACK (button_state_playing_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_playing, 0, 3, 1, 1); + gtk_widget_show (button_state_playing); + + //change framerate + GtkWidget* slider_fps = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, + 1, 30, 2); + g_signal_connect (G_OBJECT (slider_fps), "format-value", + G_CALLBACK (slider_fps_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), slider_fps, 1, 0, 1, 3); + gtk_widget_show (slider_fps); + + gtk_widget_show (table); + gtk_widget_show (window_control); + + GstElement* videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc"); + GstElement* glfilterlaplacian = gst_element_factory_make ("glfilterblur", "glfilterblur"); + GstElement* glfiltercube = gst_element_factory_make ("glfiltercube", "glfiltercube"); + GstElement* videosink = gst_element_factory_make ("glimagesink", "glimagesink"); + + GstCaps *caps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 640, + "height", G_TYPE_INT, 480, + "framerate", GST_TYPE_FRACTION, 25, 1, + "format", G_TYPE_STRING, "YV12", + NULL) ; + + gst_bin_add_many (GST_BIN (pipeline), videosrc, glfiltercube, glfilterlaplacian, videosink, NULL); + + gboolean link_ok = gst_element_link_filtered(videosrc, glfiltercube, caps) ; + gst_caps_unref(caps) ; + if(!link_ok) + { + g_warning("Failed to link videosrc to glfiltercube!\n") ; + return -1; + } + + if(!gst_element_link_many(glfiltercube, glfilterlaplacian, videosink, NULL)) + { + g_warning("Failed to link glfiltercube to videosink!\n") ; + return -1; + } + + //set window id on this event + GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), pipeline); + gst_object_unref (bus); + + //start + GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + g_print ("Failed to start up pipeline!\n"); + return -1; + } + + gtk_main(); + + return 0; +} + diff --git a/tests/examples/gl/gtk/filtervideooverlay/.gitignore b/tests/examples/gl/gtk/filtervideooverlay/.gitignore new file mode 100644 index 0000000..c5e1611 --- /dev/null +++ b/tests/examples/gl/gtk/filtervideooverlay/.gitignore @@ -0,0 +1 @@ +filtervideooverlay diff --git a/tests/examples/gl/gtk/filtervideooverlay/Makefile.am b/tests/examples/gl/gtk/filtervideooverlay/Makefile.am new file mode 100644 index 0000000..4d7f129 --- /dev/null +++ b/tests/examples/gl/gtk/filtervideooverlay/Makefile.am @@ -0,0 +1,10 @@ +noinst_PROGRAMS = filtervideooverlay + +filtervideooverlay_SOURCES = main.cpp + +filtervideooverlay_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CXXFLAGS) \ + $(GL_CFLAGS) $(GTK3_CFLAGS) +filtervideooverlay_LDADD=../libgstgtkhelper.la \ + $(GTK3_LIBS) $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) + diff --git a/tests/examples/gl/gtk/filtervideooverlay/filtervideooverlay.vcproj b/tests/examples/gl/gtk/filtervideooverlay/filtervideooverlay.vcproj new file mode 100644 index 0000000..564daa9 --- /dev/null +++ b/tests/examples/gl/gtk/filtervideooverlay/filtervideooverlay.vcproj @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/filtervideooverlay/main.cpp b/tests/examples/gl/gtk/filtervideooverlay/main.cpp new file mode 100644 index 0000000..8ca402d --- /dev/null +++ b/tests/examples/gl/gtk/filtervideooverlay/main.cpp @@ -0,0 +1,245 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#include + +#include "../gstgtk.h" + + +static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, GtkWidget* widget) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + g_print ("setting window handle %p\n", widget); + + gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), widget); + + gst_message_unref (message); + + return GST_BUS_DROP; +} + + +static void end_stream_cb(GstBus* bus, GstMessage* message, GstElement* pipeline) +{ + g_print("End of stream\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + + +static gboolean expose_cb(GtkWidget* widget, cairo_t *cr, GstElement* videosink) +{ + gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink)); + return FALSE; +} + + +static void destroy_cb(GtkWidget* widget, GdkEvent* event, GstElement* pipeline) +{ + g_print("Close\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + + +static void button_state_null_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_NULL); + g_print ("GST_STATE_NULL\n"); +} + + +static void button_state_ready_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_READY); + g_print ("GST_STATE_READY\n"); +} + + +static void button_state_paused_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); + g_print ("GST_STATE_PAUSED\n"); +} + + +static void button_state_playing_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_print ("GST_STATE_PLAYING\n"); +} + + +static gchar* slider_fps_cb (GtkScale* scale, gdouble value, GstElement* pipeline) +{ + //change the video frame rate dynamically + return g_strdup_printf ("video framerate: %0.*g", gtk_scale_get_digits (scale), value); +} + + + +gint main (gint argc, gchar *argv[]) +{ + gtk_init (&argc, &argv); + gst_init (&argc, &argv); + + GstElement* pipeline = gst_pipeline_new ("pipeline"); + + //window that contains an area where the video is drawn + GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (window, 640, 480); + gtk_window_move (GTK_WINDOW (window), 300, 10); + gtk_window_set_title (GTK_WINDOW (window), "glimagesink implement the gstvideooverlay interface"); + GdkGeometry geometry; + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window), window, &geometry, GDK_HINT_MIN_SIZE); + + //window to control the states + GtkWidget* window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL); + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window_control), window_control, &geometry, GDK_HINT_MIN_SIZE); + gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE); + gtk_window_move (GTK_WINDOW (window_control), 10, 10); + GtkWidget* grid = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (window_control), grid); + + //control state null + GtkWidget* button_state_null = gtk_button_new_with_label ("GST_STATE_NULL"); + g_signal_connect (G_OBJECT (button_state_null), "clicked", + G_CALLBACK (button_state_null_cb), pipeline); + gtk_grid_attach (GTK_GRID (grid), button_state_null, 0, 1, 1, 1); + gtk_widget_show (button_state_null); + + //control state ready + GtkWidget* button_state_ready = gtk_button_new_with_label ("GST_STATE_READY"); + g_signal_connect (G_OBJECT (button_state_ready), "clicked", + G_CALLBACK (button_state_ready_cb), pipeline); + gtk_grid_attach (GTK_GRID (grid), button_state_ready, 0, 2, 1, 1); + gtk_widget_show (button_state_ready); + + //control state paused + GtkWidget* button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED"); + g_signal_connect (G_OBJECT (button_state_paused), "clicked", + G_CALLBACK (button_state_paused_cb), pipeline); + gtk_grid_attach (GTK_GRID (grid), button_state_paused, 0, 3, 1, 1); + gtk_widget_show (button_state_paused); + + //control state playing + GtkWidget* button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING"); + g_signal_connect (G_OBJECT (button_state_playing), "clicked", + G_CALLBACK (button_state_playing_cb), pipeline); + gtk_grid_attach (GTK_GRID (grid), button_state_playing, 0, 4, 1, 1); + gtk_widget_show (button_state_playing); + + //change framerate + GtkWidget* slider_fps = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, 1, 30, 2); + g_signal_connect (G_OBJECT (slider_fps), "format-value", + G_CALLBACK (slider_fps_cb), pipeline); + gtk_grid_attach (GTK_GRID (grid), slider_fps, 1, 0, 1, 5); + gtk_widget_show (slider_fps); + + gtk_widget_show (grid); + gtk_widget_show (window_control); + + //configure the pipeline + g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(destroy_cb), pipeline); + + GstElement* videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc"); + GstElement* glfiltercube = gst_element_factory_make ("glfiltercube", "glfiltercube"); + GstElement* glfilterlaplacian = gst_element_factory_make ("glfilterlaplacian", "glfilterlaplacian"); + GstElement* videosink = gst_element_factory_make ("glimagesink", "glimagesink"); + + GstCaps *caps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 640, + "height", G_TYPE_INT, 480, + "framerate", GST_TYPE_FRACTION, 25, 1, + "format", G_TYPE_STRING, "AYUV", + NULL) ; + + gst_bin_add_many (GST_BIN (pipeline), videosrc, glfiltercube, glfilterlaplacian, videosink, NULL); + + gboolean link_ok = gst_element_link_filtered(videosrc, glfiltercube, caps) ; + gst_caps_unref(caps) ; + if(!link_ok) + { + g_warning("Failed to link videosrc to glfiltercube!\n") ; + return -1; + } + + if(!gst_element_link_many(glfiltercube, glfilterlaplacian, videosink, NULL)) + { + g_warning("Failed to link glfiltercube to videosink!\n") ; + return -1; + } + + //area where the video is drawn + GtkWidget* area = gtk_drawing_area_new(); + gtk_container_add (GTK_CONTAINER (window), area); + + gtk_widget_realize(area); + + //set window id on this event + GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, area, NULL); + gst_bus_add_signal_watch (bus); + g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), pipeline); + gst_object_unref (bus); + + //needed when being in GST_STATE_READY, GST_STATE_PAUSED + //or resizing/obscuring the window + g_signal_connect(area, "draw", G_CALLBACK(expose_cb), videosink); + + //start + GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + g_print ("Failed to start up pipeline!\n"); + return -1; + } + + gtk_widget_show_all (window); + + gtk_main(); + + return 0; +} + diff --git a/tests/examples/gl/gtk/fxtest/.gitignore b/tests/examples/gl/gtk/fxtest/.gitignore new file mode 100644 index 0000000..c355697 --- /dev/null +++ b/tests/examples/gl/gtk/fxtest/.gitignore @@ -0,0 +1,2 @@ +fxtest +pixbufdrop diff --git a/tests/examples/gl/gtk/fxtest/Makefile.am b/tests/examples/gl/gtk/fxtest/Makefile.am new file mode 100644 index 0000000..c5e5d1a --- /dev/null +++ b/tests/examples/gl/gtk/fxtest/Makefile.am @@ -0,0 +1,18 @@ +noinst_PROGRAMS = fxtest +noinst_PROGRAMS += pixbufdrop + +fxtest_SOURCES = fxtest.c + +fxtest_CFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ + $(GL_CFLAGS) $(GTK3_CFLAGS) +fxtest_LDADD=../libgstgtkhelper.la \ + $(GTK3_LIBS) $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) + +pixbufdrop_SOURCES = pixbufdrop.c + +pixbufdrop_CFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ + $(GL_CFLAGS) $(GTK3_CFLAGS) +pixbufdrop_LDADD=../libgstgtkhelper.la \ + $(GTK3_LIBS) $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) diff --git a/tests/examples/gl/gtk/fxtest/fxtest.c b/tests/examples/gl/gtk/fxtest/fxtest.c new file mode 100644 index 0000000..a9b41c1 --- /dev/null +++ b/tests/examples/gl/gtk/fxtest/fxtest.c @@ -0,0 +1,262 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Filippo Argiolas + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include +#include +#include +#include + +#include "../gstgtk.h" + +#include + + +/* TODO: use video overlay in the proper way (like suggested in docs, see gtkvideooverlay example) */ +static gboolean +expose_cb (GtkWidget * widget, gpointer data) +{ + GstVideoOverlay *overlay = + GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (data), + GST_TYPE_VIDEO_OVERLAY)); + + gst_video_overlay_set_gtk_window (overlay, widget); + + return FALSE; +} + +static void +destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline) +{ + g_message ("destroy callback"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gtk_main_quit (); +} + +static gboolean +apply_fx (GtkWidget * widget, gpointer data) +{ + gchar *fx; + GEnumClass *p_class; + +/* heeeellppppp!! */ + p_class = + G_PARAM_SPEC_ENUM (g_object_class_find_property (G_OBJECT_GET_CLASS + (G_OBJECT (data)), "effect") + )->enum_class; + + fx = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (widget)); + g_print ("setting: %s - %s\n", fx, g_enum_get_value_by_nick (p_class, + fx)->value_name); + g_object_set (G_OBJECT (data), "effect", g_enum_get_value_by_nick (p_class, + fx)->value, NULL); + return FALSE; +} + +static gboolean +play_cb (GtkWidget * widget, gpointer data) +{ + g_message ("playing"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_PLAYING); + return FALSE; +} + +static gboolean +null_cb (GtkWidget * widget, gpointer data) +{ + g_message ("nulling"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL); + return FALSE; +} + +static gboolean +ready_cb (GtkWidget * widget, gpointer data) +{ + g_message ("readying"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_READY); + return FALSE; +} + +static gboolean +pause_cb (GtkWidget * widget, gpointer data) +{ + g_message ("pausing"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_PAUSED); + return FALSE; +} + +gint +main (gint argc, gchar * argv[]) +{ + GstStateChangeReturn ret; + GstElement *pipeline; + GstElement *filter, *sink; + GstElement *sourcebin; + GError *error = NULL; + + GtkWidget *window; + GtkWidget *screen; + GtkWidget *vbox, *combo; + GtkWidget *hbox; + GtkWidget *play, *pause, *null, *ready; + + gchar **source_desc_array = NULL; + gchar *source_desc = NULL; + + GOptionContext *context; + GOptionEntry options[] = { + {"source-bin", 's', 0, G_OPTION_ARG_STRING_ARRAY, &source_desc_array, + "Use a custom source bin description (gst-launch style)", NULL} + , + {NULL} + }; + + context = g_option_context_new (NULL); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_add_group (context, gst_init_get_option_group ()); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_print ("Inizialization error: %s\n", GST_STR_NULL (error->message)); + return -1; + } + g_option_context_free (context); + + if (source_desc_array != NULL) { + source_desc = g_strjoinv (" ", source_desc_array); + g_strfreev (source_desc_array); + } + if (source_desc == NULL) { + source_desc = + g_strdup + ("videotestsrc ! video/x-raw, width=352, height=288 ! identity"); + } + + sourcebin = + gst_parse_bin_from_description (g_strdup (source_desc), TRUE, &error); + g_free (source_desc); + if (error) { + g_print ("Error while parsing source bin description: %s\n", + GST_STR_NULL (error->message)); + return -1; + } + + g_set_application_name ("gst-gl-effects test app"); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 3); + + pipeline = gst_pipeline_new ("pipeline"); + + filter = gst_element_factory_make ("gleffects", "flt"); + sink = gst_element_factory_make ("glimagesink", "glsink"); + + gst_bin_add_many (GST_BIN (pipeline), sourcebin, filter, sink, NULL); + + if (!gst_element_link_many (sourcebin, filter, sink, NULL)) { + g_print ("Failed to link one or more elements!\n"); + return -1; + } + + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (destroy_cb), pipeline); + g_signal_connect (G_OBJECT (window), "destroy-event", + G_CALLBACK (destroy_cb), pipeline); + + screen = gtk_drawing_area_new (); + + gtk_widget_set_size_request (screen, 640, 480); // 500 x 376 + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + + gtk_box_pack_start (GTK_BOX (vbox), screen, TRUE, TRUE, 0); + + combo = gtk_combo_box_text_new (); + + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "identity"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "mirror"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "squeeze"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "stretch"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "fisheye"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "twirl"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "bulge"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "tunnel"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "square"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "heat"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "xpro"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "lumaxpro"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "sepia"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "xray"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "sin"); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "glow"); + + g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (apply_fx), filter); + + gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + + play = gtk_button_new_with_label ("PLAY"); + + g_signal_connect (G_OBJECT (play), "clicked", G_CALLBACK (play_cb), pipeline); + + pause = gtk_button_new_with_label ("PAUSE"); + + g_signal_connect (G_OBJECT (pause), "clicked", + G_CALLBACK (pause_cb), pipeline); + + null = gtk_button_new_with_label ("NULL"); + + g_signal_connect (G_OBJECT (null), "clicked", G_CALLBACK (null_cb), pipeline); + + ready = gtk_button_new_with_label ("READY"); + + g_signal_connect (G_OBJECT (ready), "clicked", + G_CALLBACK (ready_cb), pipeline); + + gtk_box_pack_start (GTK_BOX (hbox), null, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), ready, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), play, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), pause, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (window), vbox); + + g_signal_connect (screen, "realize", G_CALLBACK (expose_cb), pipeline); + + ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) { + g_print ("Failed to start up pipeline!\n"); + return -1; + } + + gtk_widget_show_all (GTK_WIDGET (window)); + + gtk_main (); + + return 0; +} diff --git a/tests/examples/gl/gtk/fxtest/fxtest.vcproj b/tests/examples/gl/gtk/fxtest/fxtest.vcproj new file mode 100644 index 0000000..05a2de0 --- /dev/null +++ b/tests/examples/gl/gtk/fxtest/fxtest.vcproj @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/fxtest/pixbufdrop.c b/tests/examples/gl/gtk/fxtest/pixbufdrop.c new file mode 100644 index 0000000..4b26c3e --- /dev/null +++ b/tests/examples/gl/gtk/fxtest/pixbufdrop.c @@ -0,0 +1,325 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Filippo Argiolas + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include +#include +#include +#include + +#include "../gstgtk.h" + +#include + +static gint delay = 0; +static gint saveddelay = 0; +static gint method = 1; + +struct _SourceData +{ + gpointer data; + gpointer nick; + gpointer value; +}; +typedef struct _SourceData SourceData; + +static GstBusSyncReply +create_window (GstBus * bus, GstMessage * message, GtkWidget * widget) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC + (message)), widget); + + gst_message_unref (message); + + return GST_BUS_DROP; +} + +static void +message_cb (GstBus * bus, GstMessage * message, GstElement * pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gtk_main_quit (); +} + +static gboolean +expose_cb (GtkWidget * widget, cairo_t * cr, GstElement * videosink) +{ + gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink)); + return FALSE; +} + +static void +destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline) +{ + g_message ("destroy callback"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gtk_main_quit (); +} + +static gboolean +play_cb (GtkWidget * widget, gpointer data) +{ + g_message ("playing"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_PLAYING); + return FALSE; +} + +static gboolean +null_cb (GtkWidget * widget, gpointer data) +{ + g_message ("nulling"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL); + return FALSE; +} + +static gboolean +ready_cb (GtkWidget * widget, gpointer data) +{ + g_message ("readying"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_READY); + return FALSE; +} + +static gboolean +pause_cb (GtkWidget * widget, gpointer data) +{ + g_message ("pausing"); + gst_element_set_state (GST_ELEMENT (data), GST_STATE_PAUSED); + return FALSE; +} + +static gboolean +set_location_delayed (gpointer data) +{ + SourceData *sdata = (SourceData *) data; + delay--; + g_print ("%d\n", delay); + if (delay > 0) { + return TRUE; + } + g_object_set (G_OBJECT (sdata->data), sdata->nick, sdata->value, NULL); + delay = saveddelay; + return FALSE; +} + +static void +on_drag_data_received (GtkWidget * widget, + GdkDragContext * context, int x, int y, + GtkSelectionData * seldata, guint inf, guint time, gpointer data) +{ + SourceData *userdata = g_new0 (SourceData, 1); +#ifdef G_OS_WIN32 + gchar *filename = + g_filename_from_uri ((const gchar *) seldata->data, NULL, NULL); +#else + GdkPixbufFormat *format; + gchar **uris = gtk_selection_data_get_uris (seldata); + gchar *filename = NULL; + + g_return_if_fail (uris != NULL); + filename = g_filename_from_uri (uris[0], NULL, NULL); + g_return_if_fail (filename != NULL); + format = gdk_pixbuf_get_file_info (filename, NULL, NULL); + g_return_if_fail (format); + g_print ("received %s image: %s\n", filename, + gdk_pixbuf_format_get_name (format)); +#endif + + userdata->nick = (gchar *) "location"; + userdata->value = g_strdup (filename); + userdata->data = data; + saveddelay = delay; + if (delay > 0) { + g_print ("%d\n", delay); + g_timeout_add_seconds (1, set_location_delayed, userdata); + } else + g_object_set (G_OBJECT (userdata->data), userdata->nick, userdata->value, + NULL); + g_free (filename); +} + + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *pipeline; + GstElement *filter, *sink; + GstElement *sourcebin; + GstBus *bus; + GError *error = NULL; + + GtkWidget *window; + GtkWidget *screen; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *play, *pause, *null, *ready; + + gchar **source_desc_array = NULL; + gchar *source_desc = NULL; + + GOptionContext *context; + GOptionEntry options[] = { + {"source-bin", 's', 0, G_OPTION_ARG_STRING_ARRAY, &source_desc_array, + "Use a custom source bin description (gst-launch style)", NULL} + , + {"method", 'm', 0, G_OPTION_ARG_INT, &method, + "1 for gstdifferencematte, 2 for gloverlay", "M"} + , + {"delay", 'd', 0, G_OPTION_ARG_INT, &delay, + "Wait N seconds before to send the image to gstreamer (useful with differencematte)", + "N"} + , + {NULL} + }; + + context = g_option_context_new (NULL); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_add_group (context, gst_init_get_option_group ()); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_print ("Inizialization error: %s\n", GST_STR_NULL (error->message)); + return -1; + } + g_option_context_free (context); + + if (source_desc_array != NULL) { + source_desc = g_strjoinv (" ", source_desc_array); + g_strfreev (source_desc_array); + } + if (source_desc == NULL) { + source_desc = + g_strdup + ("videotestsrc ! video/x-raw, width=352, height=288 ! identity"); + } + + sourcebin = + gst_parse_bin_from_description (g_strdup (source_desc), TRUE, &error); + g_free (source_desc); + if (error) { + g_print ("Error while parsing source bin description: %s\n", + GST_STR_NULL (error->message)); + return -1; + } + + g_set_application_name ("gst-gl-effects test app"); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 3); + + pipeline = gst_pipeline_new ("pipeline"); + + if (method == 2) { + filter = gst_element_factory_make ("gloverlay", "flt"); + } else { + filter = gst_element_factory_make ("gldifferencematte", "flt"); + } + sink = gst_element_factory_make ("glimagesink", "glsink"); + + gst_bin_add_many (GST_BIN (pipeline), sourcebin, filter, sink, NULL); + + if (!gst_element_link_many (sourcebin, filter, sink, NULL)) { + g_print ("Failed to link one or more elements!\n"); + return -1; + } + + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (destroy_cb), pipeline); + g_signal_connect (G_OBJECT (window), "destroy-event", + G_CALLBACK (destroy_cb), pipeline); + + screen = gtk_drawing_area_new (); + + gtk_widget_set_size_request (screen, 640, 480); // 500 x 376 + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + + gtk_box_pack_start (GTK_BOX (vbox), screen, TRUE, TRUE, 0); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + + play = gtk_button_new_with_label ("PLAY"); + + g_signal_connect (G_OBJECT (play), "clicked", G_CALLBACK (play_cb), pipeline); + + pause = gtk_button_new_with_label ("PAUSE"); + + g_signal_connect (G_OBJECT (pause), "clicked", + G_CALLBACK (pause_cb), pipeline); + + null = gtk_button_new_with_label ("NULL"); + + g_signal_connect (G_OBJECT (null), "clicked", G_CALLBACK (null_cb), pipeline); + + ready = gtk_button_new_with_label ("READY"); + + g_signal_connect (G_OBJECT (ready), "clicked", + G_CALLBACK (ready_cb), pipeline); + + gtk_box_pack_start (GTK_BOX (hbox), null, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), ready, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), play, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), pause, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (window), vbox); + + gtk_widget_realize (screen); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, screen, + NULL); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message::error", G_CALLBACK (message_cb), pipeline); + g_signal_connect (bus, "message::warning", G_CALLBACK (message_cb), pipeline); + g_signal_connect (bus, "message::eos", G_CALLBACK (message_cb), pipeline); + gst_object_unref (bus); + g_signal_connect (screen, "draw", G_CALLBACK (expose_cb), sink); + + gtk_drag_dest_set (screen, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY); + gtk_drag_dest_add_uri_targets (screen); + + g_signal_connect (screen, "drag-data-received", + G_CALLBACK (on_drag_data_received), filter); + + gtk_widget_show_all (GTK_WIDGET (window)); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + gtk_main (); + + return 0; +} diff --git a/tests/examples/gl/gtk/gstgtk.c b/tests/examples/gl/gtk/gstgtk.c new file mode 100644 index 0000000..b13304c --- /dev/null +++ b/tests/examples/gl/gtk/gstgtk.c @@ -0,0 +1,71 @@ +/* + * GStreamer + * Copyright (C) 2009 David A. Schleef + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstgtk.h" + +#if GST_GL_HAVE_WINDOW_WIN32 && defined(GDK_WINDOWING_WIN32) +#include +#endif +#if GST_GL_HAVE_WINDOW_X11 && defined(GDK_WINDOWING_X11) +#include +#include +#endif +#if GST_GL_HAVE_WINDOW_COCOA && defined(GDK_WINDOWING_QUARTZ) +#include +#endif + + +void +gst_video_overlay_set_gtk_window (GstVideoOverlay * videooverlay, + GtkWidget * widget) +{ + GdkWindow *window; + GdkDisplay *display; + const gchar *user_choice = g_getenv ("GST_GL_WINDOW"); + + window = gtk_widget_get_window (widget); + display = gdk_window_get_display (window); + +#if GST_GL_HAVE_WINDOW_WIN32 && defined(GDK_WINDOWING_WIN32) + if (GDK_IS_WIN32_DISPLAY (display) && (!user_choice + || g_strcmp0 (user_choice, "win32") == 0)) { + gst_video_overlay_set_window_handle (videooverlay, + (guintptr) GDK_WINDOW_HWND (window)); + } else +#endif +#if GST_GL_HAVE_WINDOW_COCOA && defined(GDK_WINDOWING_QUARTZ) + if (GDK_IS_QUARTZ_DISPLAY (display) && (!user_choice + || g_strcmp0 (user_choice, "cocoa") == 0)) { + gst_video_overlay_set_window_handle (videooverlay, (guintptr) + gdk_quartz_window_get_nswindow (window)); + } else +#endif +#if GST_GL_HAVE_WINDOW_X11 && defined(GDK_WINDOWING_X11) + if (GDK_IS_X11_DISPLAY (display) && (!user_choice + || g_strcmp0 (user_choice, "x11") == 0)) { + gst_video_overlay_set_window_handle (videooverlay, GDK_WINDOW_XID (window)); + } else +#endif + g_error ("Unsupported Gtk+ backend"); +} diff --git a/tests/examples/gl/gtk/gstgtk.h b/tests/examples/gl/gtk/gstgtk.h new file mode 100644 index 0000000..4ef147f --- /dev/null +++ b/tests/examples/gl/gtk/gstgtk.h @@ -0,0 +1,34 @@ +/* + * GStreamer + * Copyright (C) 2009 David A. Schleef + * + * 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. + */ + +#ifndef __GST_GTK_H__ +#define __GST_GTK_H__ + +#include +#include + +G_BEGIN_DECLS + +void gst_video_overlay_set_gtk_window (GstVideoOverlay *videooverlay, GtkWidget *window); + +G_END_DECLS + +#endif + diff --git a/tests/examples/gl/gtk/gtk.sln b/tests/examples/gl/gtk/gtk.sln new file mode 100644 index 0000000..5bf66dc --- /dev/null +++ b/tests/examples/gl/gtk/gtk.sln @@ -0,0 +1,50 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filtervideooverlay", "filtervideooverlay\filtervideooverlay.vcproj", "{F9CC027E-CC9F-4B34-AA8F-58852EC32CD0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtkvideooverlay", "gtkvideooverlay\gtkvideooverlay.vcproj", "{E83070C2-58E4-48AE-AEB3-A4580EDE1212}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fxtest", "fxtest\fxtest.vcproj", "{59075FDD-68CD-4F1A-948B-46D142800798}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pixbufdrop", "pixbufdrop\pixbufdrop.vcproj", "{09F68B62-1D4C-4C24-A6AD-AA76A9F3237C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filternovideooverlay", "filternovideooverlay\filternovideooverlay.vcproj", "{F41F3034-3E0B-4630-8D1E-35E14C606863}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "switchvideooverlay", "switchvideooverlay\switchvideooverlay.vcproj", "{BA78B4B4-3268-483E-9676-911E29FD2C69}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F9CC027E-CC9F-4B34-AA8F-58852EC32CD0}.Debug|Win32.ActiveCfg = Debug|Win32 + {F9CC027E-CC9F-4B34-AA8F-58852EC32CD0}.Debug|Win32.Build.0 = Debug|Win32 + {F9CC027E-CC9F-4B34-AA8F-58852EC32CD0}.Release|Win32.ActiveCfg = Release|Win32 + {F9CC027E-CC9F-4B34-AA8F-58852EC32CD0}.Release|Win32.Build.0 = Release|Win32 + {E83070C2-58E4-48AE-AEB3-A4580EDE1212}.Debug|Win32.ActiveCfg = Debug|Win32 + {E83070C2-58E4-48AE-AEB3-A4580EDE1212}.Debug|Win32.Build.0 = Debug|Win32 + {E83070C2-58E4-48AE-AEB3-A4580EDE1212}.Release|Win32.ActiveCfg = Release|Win32 + {E83070C2-58E4-48AE-AEB3-A4580EDE1212}.Release|Win32.Build.0 = Release|Win32 + {59075FDD-68CD-4F1A-948B-46D142800798}.Debug|Win32.ActiveCfg = Debug|Win32 + {59075FDD-68CD-4F1A-948B-46D142800798}.Debug|Win32.Build.0 = Debug|Win32 + {59075FDD-68CD-4F1A-948B-46D142800798}.Release|Win32.ActiveCfg = Release|Win32 + {59075FDD-68CD-4F1A-948B-46D142800798}.Release|Win32.Build.0 = Release|Win32 + {09F68B62-1D4C-4C24-A6AD-AA76A9F3237C}.Debug|Win32.ActiveCfg = Debug|Win32 + {09F68B62-1D4C-4C24-A6AD-AA76A9F3237C}.Debug|Win32.Build.0 = Debug|Win32 + {09F68B62-1D4C-4C24-A6AD-AA76A9F3237C}.Release|Win32.ActiveCfg = Release|Win32 + {09F68B62-1D4C-4C24-A6AD-AA76A9F3237C}.Release|Win32.Build.0 = Release|Win32 + {F41F3034-3E0B-4630-8D1E-35E14C606863}.Debug|Win32.ActiveCfg = Debug|Win32 + {F41F3034-3E0B-4630-8D1E-35E14C606863}.Debug|Win32.Build.0 = Debug|Win32 + {F41F3034-3E0B-4630-8D1E-35E14C606863}.Release|Win32.ActiveCfg = Release|Win32 + {F41F3034-3E0B-4630-8D1E-35E14C606863}.Release|Win32.Build.0 = Release|Win32 + {BA78B4B4-3268-483E-9676-911E29FD2C69}.Debug|Win32.ActiveCfg = Debug|Win32 + {BA78B4B4-3268-483E-9676-911E29FD2C69}.Debug|Win32.Build.0 = Debug|Win32 + {BA78B4B4-3268-483E-9676-911E29FD2C69}.Release|Win32.ActiveCfg = Release|Win32 + {BA78B4B4-3268-483E-9676-911E29FD2C69}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tests/examples/gl/gtk/gtkvideooverlay/.gitignore b/tests/examples/gl/gtk/gtkvideooverlay/.gitignore new file mode 100644 index 0000000..e4c3e3f --- /dev/null +++ b/tests/examples/gl/gtk/gtkvideooverlay/.gitignore @@ -0,0 +1 @@ +gtkvideooverlay diff --git a/tests/examples/gl/gtk/gtkvideooverlay/Makefile.am b/tests/examples/gl/gtk/gtkvideooverlay/Makefile.am new file mode 100644 index 0000000..c9dba8a --- /dev/null +++ b/tests/examples/gl/gtk/gtkvideooverlay/Makefile.am @@ -0,0 +1,10 @@ +noinst_PROGRAMS = gtkvideooverlay + +gtkvideooverlay_SOURCES = main.cpp + +gtkvideooverlay_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CXXFLAGS) \ + $(GL_CFLAGS) $(GTK3_CFLAGS) +gtkvideooverlay_LDADD=../libgstgtkhelper.la \ + $(GTK3_LIBS) $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) + diff --git a/tests/examples/gl/gtk/gtkvideooverlay/gtkvideooverlay.vcproj b/tests/examples/gl/gtk/gtkvideooverlay/gtkvideooverlay.vcproj new file mode 100644 index 0000000..f818fc6 --- /dev/null +++ b/tests/examples/gl/gtk/gtkvideooverlay/gtkvideooverlay.vcproj @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/gtkvideooverlay/main.cpp b/tests/examples/gl/gtk/gtkvideooverlay/main.cpp new file mode 100644 index 0000000..7024cfc --- /dev/null +++ b/tests/examples/gl/gtk/gtkvideooverlay/main.cpp @@ -0,0 +1,230 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#include + +#include "../gstgtk.h" + + +static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, GtkWidget* widget) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + g_print ("setting window handle\n"); + + //do not call gdk_window_ensure_native for the first time here because + //we are in a different thread than the main thread + //(and the main thread the onne) + gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), widget); + + gst_message_unref (message); + + return GST_BUS_DROP; +} + + +static void end_stream_cb(GstBus* bus, GstMessage* message, GstElement* pipeline) +{ + g_print("End of stream\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + +static gboolean draw_cb(GtkWidget* widget, cairo_t *cr, GstElement* videosink) +{ + g_print ("draw_cb\n"); + gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink)); + return FALSE; +} + + +static void destroy_cb(GtkWidget* widget, GdkEvent* event, GstElement* pipeline) +{ + g_print("Close\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + + +static void button_state_null_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_NULL); + g_print ("GST_STATE_NULL\n"); +} + + +static void button_state_ready_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_READY); + g_print ("GST_STATE_READY\n"); +} + + +static void button_state_paused_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); + g_print ("GST_STATE_PAUSED\n"); +} + + +static void button_state_playing_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_print ("GST_STATE_PLAYING\n"); +} + + +static gchar* slider_fps_cb (GtkScale* scale, gdouble value, GstElement* pipeline) +{ + //change the video frame rate dynamically + return g_strdup_printf ("video framerate: %0.*g", gtk_scale_get_digits (scale), value); +} + + + +gint main (gint argc, gchar *argv[]) +{ + GtkWidget *area; + gst_init (&argc, &argv); + gtk_init (&argc, &argv); + + GstElement* pipeline = gst_pipeline_new ("pipeline"); + GstElement* videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc"); + GstElement* videosink = gst_element_factory_make ("glimagesink", "glimagesink"); + + gst_bin_add_many (GST_BIN (pipeline), videosrc, videosink, NULL); + + gboolean link_ok = gst_element_link_many(videosrc, videosink, NULL) ; + if(!link_ok) + { + g_warning("Failed to link an element!\n") ; + return -1; + } + + //set window id on this event + GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), pipeline); + + gst_element_set_state(pipeline, GST_STATE_READY); + + area = gtk_drawing_area_new(); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, area, NULL); + gst_object_unref (bus); + + //window that contains an area where the video is drawn + GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (window, 640, 480); + gtk_window_move (GTK_WINDOW (window), 300, 10); + gtk_window_set_title (GTK_WINDOW (window), "glimagesink implement the gstvideooverlay interface"); + GdkGeometry geometry; + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window), window, &geometry, GDK_HINT_MIN_SIZE); + + //window to control the states + GtkWidget* window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL); + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window_control), window_control, &geometry, GDK_HINT_MIN_SIZE); + gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE); + gtk_window_move (GTK_WINDOW (window_control), 10, 10); + GtkWidget* table = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (window_control), table); + + //control state null + GtkWidget* button_state_null = gtk_button_new_with_label ("GST_STATE_NULL"); + g_signal_connect (G_OBJECT (button_state_null), "clicked", + G_CALLBACK (button_state_null_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_null, 0, 0, 1, 1); + gtk_widget_show (button_state_null); + + //control state ready + GtkWidget* button_state_ready = gtk_button_new_with_label ("GST_STATE_READY"); + g_signal_connect (G_OBJECT (button_state_ready), "clicked", + G_CALLBACK (button_state_ready_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_ready, 0, 1, 1, 1); + gtk_widget_show (button_state_ready); + + //control state paused + GtkWidget* button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED"); + g_signal_connect (G_OBJECT (button_state_paused), "clicked", + G_CALLBACK (button_state_paused_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_paused, 0, 2, 1, 1); + gtk_widget_show (button_state_paused); + + //control state playing + GtkWidget* button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING"); + g_signal_connect (G_OBJECT (button_state_playing), "clicked", + G_CALLBACK (button_state_playing_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_playing, 0, 3, 1, 1); + gtk_widget_show (button_state_playing); + + //change framerate + GtkWidget* slider_fps = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, + 1, 30, 2); + g_signal_connect (G_OBJECT (slider_fps), "format-value", + G_CALLBACK (slider_fps_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), slider_fps, 1, 0, 1, 4); + gtk_widget_show (slider_fps); + + gtk_widget_show (table); + gtk_widget_show (window_control); + + //configure the pipeline + g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(destroy_cb), pipeline); + + //area where the video is drawn + gtk_container_add (GTK_CONTAINER (window), area); + + gtk_widget_realize(area); + + //needed when being in GST_STATE_READY, GST_STATE_PAUSED + //or resizing/obscuring the window + g_signal_connect(area, "draw", G_CALLBACK(draw_cb), videosink); + + gtk_widget_show_all (window); + + gst_element_set_state(pipeline, GST_STATE_PLAYING); + + gtk_main(); + + return 0; +} + diff --git a/tests/examples/gl/gtk/pixbufdrop/pixbufdrop.vcproj b/tests/examples/gl/gtk/pixbufdrop/pixbufdrop.vcproj new file mode 100644 index 0000000..d38ecc7 --- /dev/null +++ b/tests/examples/gl/gtk/pixbufdrop/pixbufdrop.vcproj @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/switchvideooverlay/.gitignore b/tests/examples/gl/gtk/switchvideooverlay/.gitignore new file mode 100644 index 0000000..64c6de4 --- /dev/null +++ b/tests/examples/gl/gtk/switchvideooverlay/.gitignore @@ -0,0 +1 @@ +switchvideooverlay diff --git a/tests/examples/gl/gtk/switchvideooverlay/Makefile.am b/tests/examples/gl/gtk/switchvideooverlay/Makefile.am new file mode 100644 index 0000000..12e25e7 --- /dev/null +++ b/tests/examples/gl/gtk/switchvideooverlay/Makefile.am @@ -0,0 +1,10 @@ +noinst_PROGRAMS = switchvideooverlay + +switchvideooverlay_SOURCES = main.cpp + +switchvideooverlay_CXXFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CXXFLAGS) \ + $(GL_CFLAGS) $(GTK3_CFLAGS) +switchvideooverlay_LDADD=../libgstgtkhelper.la \ + $(GTK3_LIBS) $(GST_PLUGINS_GL_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) + diff --git a/tests/examples/gl/gtk/switchvideooverlay/main.cpp b/tests/examples/gl/gtk/switchvideooverlay/main.cpp new file mode 100644 index 0000000..440d5ce --- /dev/null +++ b/tests/examples/gl/gtk/switchvideooverlay/main.cpp @@ -0,0 +1,246 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#include + +#include "../gstgtk.h" + + +static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, GtkWidget* widget) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + g_print ("setting window handle %p\n", widget); + + gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), widget); + + gst_message_unref (message); + + return GST_BUS_DROP; +} + + +static void end_stream_cb(GstBus* bus, GstMessage* message, GstElement* pipeline) +{ + g_print("End of stream\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + + +static gboolean expose_cb(GtkWidget* widget, cairo_t *cr, GstElement* videosink) +{ + g_print ("expose %p\n", widget); + g_print ("event mask: 0x%x, button_press 0x%x\n", gtk_widget_get_events (widget), GDK_BUTTON_PRESS_MASK); + gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink)); + return FALSE; +} + +static gboolean on_click_drawing_area(GtkWidget* widget, GdkEventButton* event, GstElement* videosink) +{ + g_print ("switch the drawing area %p\n", widget); + gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (videosink), widget); + gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink)); + return FALSE; +} + + +static void destroy_cb(GtkWidget* widget, GdkEvent* event, GstElement* pipeline) +{ + g_print("Close\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref(pipeline); + + gtk_main_quit(); +} + + +static void button_state_null_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_NULL); + g_print ("GST_STATE_NULL\n"); +} + + +static void button_state_ready_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_READY); + g_print ("GST_STATE_READY\n"); +} + + +static void button_state_paused_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); + g_print ("GST_STATE_PAUSED\n"); +} + + +static void button_state_playing_cb(GtkWidget* widget, GstElement* pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_print ("GST_STATE_PLAYING\n"); +} + +static void area_realize_cb(GtkWidget* widget, gpointer data) +{ + g_print ("realize %p\n", widget); + if (!gdk_window_ensure_native (gtk_widget_get_window (widget))) + g_error ("Failed to create native window!"); + + //avoid flickering when resizing or obscuring the main window + gtk_widget_set_app_paintable(widget, TRUE); + gtk_widget_set_double_buffered(widget, FALSE); +} + + +gint main (gint argc, gchar *argv[]) +{ + gtk_init (&argc, &argv); + gst_init (&argc, &argv); + + GstElement* pipeline = gst_pipeline_new ("pipeline"); + + //window that contains several ares where the video is drawn + GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (window, 640, 240); + gtk_window_move (GTK_WINDOW (window), 300, 10); + gtk_window_set_title (GTK_WINDOW (window), "click on left, right or outside the main window to switch the drawing area"); + GdkGeometry geometry; + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window), window, &geometry, GDK_HINT_MIN_SIZE); + + //window to control the states + GtkWidget* window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL); + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window_control), window_control, &geometry, GDK_HINT_MIN_SIZE); + gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE); + gtk_window_move (GTK_WINDOW (window_control), 10, 10); + GtkWidget* table = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (window_control), table); + + //control state null + GtkWidget* button_state_null = gtk_button_new_with_label ("GST_STATE_NULL"); + g_signal_connect (G_OBJECT (button_state_null), "clicked", + G_CALLBACK (button_state_null_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_null, 0, 0, 1, 1); + gtk_widget_show (button_state_null); + + //control state ready + GtkWidget* button_state_ready = gtk_button_new_with_label ("GST_STATE_READY"); + g_signal_connect (G_OBJECT (button_state_ready), "clicked", + G_CALLBACK (button_state_ready_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_ready, 0, 1, 1, 1); + gtk_widget_show (button_state_ready); + + //control state paused + GtkWidget* button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED"); + g_signal_connect (G_OBJECT (button_state_paused), "clicked", + G_CALLBACK (button_state_paused_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_paused, 0, 2, 1, 1); + gtk_widget_show (button_state_paused); + + //control state playing + GtkWidget* button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING"); + g_signal_connect (G_OBJECT (button_state_playing), "clicked", + G_CALLBACK (button_state_playing_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_playing, 0, 3, 1, 1); + gtk_widget_show (button_state_playing); + + gtk_widget_show (table); + gtk_widget_show (window_control); + + //configure the pipeline + g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(destroy_cb), pipeline); + + GstElement* videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc"); + GstElement* videosink = gst_element_factory_make ("glimagesink", "glimagesink"); + + gst_bin_add_many (GST_BIN (pipeline), videosrc, videosink, NULL); + + gboolean link_ok = gst_element_link_many(videosrc, videosink, NULL); + if(!link_ok) + { + g_warning("Failed to link videosrc to videosink!\n") ; + return -1; + } + + //areas where the video is drawn + GtkWidget* table_areas = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (window), table_areas); + GtkWidget* area_top_left = gtk_drawing_area_new(); + gtk_widget_add_events(area_top_left, GDK_BUTTON_PRESS_MASK); + gtk_widget_set_size_request (area_top_left, 320, 240); + gtk_grid_attach (GTK_GRID (table_areas), area_top_left, 0, 0, 1, 1); + GtkWidget* area_top_right = gtk_drawing_area_new(); + gtk_widget_add_events(area_top_right, GDK_BUTTON_PRESS_MASK); + gtk_widget_set_size_request (area_top_right, 320, 240); + gtk_grid_attach (GTK_GRID (table_areas), area_top_right, 1, 0, 1, 1); + + //set window id on this event + g_signal_connect(area_top_left, "realize", G_CALLBACK(area_realize_cb), NULL); + g_signal_connect(area_top_right, "realize", G_CALLBACK(area_realize_cb), NULL); + + gtk_widget_realize(area_top_left); + gtk_widget_realize(area_top_right); + + GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, area_top_right, NULL); + gst_bus_add_signal_watch (bus); + g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), pipeline); + g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), pipeline); + gst_object_unref (bus); + + //needed when being in GST_STATE_READY, GST_STATE_PAUSED + //or resizing/obscuring the window + g_signal_connect(area_top_left, "draw", G_CALLBACK(expose_cb), videosink); + g_signal_connect(area_top_right, "draw", G_CALLBACK(expose_cb), videosink); + + //switch the drawing area + g_signal_connect(area_top_left, "button-press-event", G_CALLBACK(on_click_drawing_area), videosink); + g_signal_connect(area_top_right, "button-press-event", G_CALLBACK(on_click_drawing_area), videosink); + + gtk_widget_show_all (window); + + gst_element_set_state(pipeline, GST_STATE_PLAYING); + + gtk_main(); + + return 0; +} + diff --git a/tests/examples/gl/gtk/switchvideooverlay/switchvideooverlay.vcproj b/tests/examples/gl/gtk/switchvideooverlay/switchvideooverlay.vcproj new file mode 100644 index 0000000..8e48bbe --- /dev/null +++ b/tests/examples/gl/gtk/switchvideooverlay/switchvideooverlay.vcproj @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/qt/Makefile.am b/tests/examples/gl/qt/Makefile.am new file mode 100644 index 0000000..629415b --- /dev/null +++ b/tests/examples/gl/qt/Makefile.am @@ -0,0 +1,2 @@ + +#SUBDIRS = videooverlay qglwidgetvideooverlay mousevideooverlay diff --git a/tests/examples/gl/qt/README b/tests/examples/gl/qt/README new file mode 100644 index 0000000..aa843d6 --- /dev/null +++ b/tests/examples/gl/qt/README @@ -0,0 +1,23 @@ +--- Description of the Qt examples --- + +- mousevideooverlay: +Show how to use the videooverlay interface through Qt. +The cube is rotating when moving the mouse (+ click maintained) + +- qglvideooverlay: +Show how to use the videooverlay interface through Qt. +The cube is rotating automatically into a QGLWidget + +- videovdieooverlay: +Show how to use the videooverlay interface through Qt. +The video is displayed as normal 2D scene. +The window is dynamically resized to have the same size as the original video. + +--- How to build the Qt examples --- + +sudo apt-get install g++ +sudo apt-get install libqt4-dev +cd qglvideooverlay +qmake +make +./debug/qglvideooverlay diff --git a/tests/examples/gl/qt/mousevideooverlay/gstthread.cpp b/tests/examples/gl/qt/mousevideooverlay/gstthread.cpp new file mode 100644 index 0000000..ce14a47 --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/gstthread.cpp @@ -0,0 +1,67 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include "gstthread.h" + +GstThread::GstThread(const WId winId, const QString videoLocation, QObject *parent): + QThread(parent), + m_winId(winId), + m_videoLocation(videoLocation) +{ +} + +GstThread::~GstThread() +{ +} + +void GstThread::exposeRequested() +{ + m_pipeline->exposeRequested(); +} + +void GstThread::onMouseMove() +{ + m_pipeline->rotateRequested(); +} + +void GstThread::show() +{ + emit showRequested(); +} + +void GstThread::stop() +{ + m_pipeline->stop(); +} + +void GstThread::run() +{ + m_pipeline = new Pipeline(m_winId, m_videoLocation); + connect(m_pipeline, SIGNAL(showRequested()), this, SLOT(show())); + m_pipeline->start(); //it runs the gmainloop on win32 + +#ifndef WIN32 + //works like the gmainloop on linux (GstEvent are handled) + connect(m_pipeline, SIGNAL(stopRequested()), this, SLOT(quit())); + exec(); +#endif + + m_pipeline->unconfigure(); +} diff --git a/tests/examples/gl/qt/mousevideooverlay/gstthread.h b/tests/examples/gl/qt/mousevideooverlay/gstthread.h new file mode 100644 index 0000000..74f1322 --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/gstthread.h @@ -0,0 +1,56 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#ifndef GSTTHREAD_H +#define GSTTHREAD_H + +#include +#include + +#include "pipeline.h" + +class GstThread : public QThread +{ + Q_OBJECT + +public: + GstThread(const WId winId, const QString videoLocation, QObject *parent = 0); + ~GstThread(); + +public slots: + void exposeRequested(); + void onMouseMove(); + void show(); + void stop(); + +signals: + void showRequested(); + +protected: + void run(); + +private: + const WId m_winId; + const QString m_videoLocation; + Pipeline* m_pipeline; + +}; + +#endif diff --git a/tests/examples/gl/qt/mousevideooverlay/main.cpp b/tests/examples/gl/qt/mousevideooverlay/main.cpp new file mode 100644 index 0000000..0cec1b9 --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/main.cpp @@ -0,0 +1,39 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include "qrenderer.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit())); + + QString videolcoation = QFileDialog::getOpenFileName(0, "Select a video file", + ".", "Format (*.avi *.mkv *.ogg *.asf *.mov)"); + + if (videolcoation.isEmpty()) + return -1; + + QRenderer w(videolcoation); + w.setWindowTitle("glimagesink implements the gstvideooverlay interface"); + + return a.exec(); +} diff --git a/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.pri b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.pri new file mode 100644 index 0000000..75b23cd --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.pri @@ -0,0 +1,10 @@ +#Header files +HEADERS += ./gstthread.h \ + ./pipeline.h \ + ./qrenderer.h + +#Source files +SOURCES += ./gstthread.cpp \ + ./main.cpp \ + ./pipeline.cpp \ + ./qrenderer.cpp diff --git a/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.pro b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.pro new file mode 100644 index 0000000..efd69cd --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.pro @@ -0,0 +1,54 @@ +TEMPLATE = app +TARGET = mousevideooverlay +DESTDIR = ./debug +CONFIG += debug gui widget +DEFINES += UNICODE + +win32 { +DEFINES += WIN32 +INCLUDEPATH += ./GeneratedFiles \ + ./GeneratedFiles/Debug \ + C:/gstreamer/include \ + C:/gstreamer/include/libxml2 \ + C:/gstreamer/include/glib-2.0 \ + C:/gstreamer/lib/glib-2.0/include \ + C:/gstreamer/include/gstreamer-1.0 +LIBS += -L"C:/gstreamer/lib" \ + -L"C:/gstreamer/bin" \ + -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lopengl32 \ + -lglu32 +} + +unix { +DEFINES += UNIX +INCLUDEPATH += GeneratedFiles \ + GeneratedFiles/Debug \ + /usr/include/gstreamer-1.0 \ + /usr/local/include/gstreamer-1.0 \ + /usr/include/glib-2.0 \ + /usr/lib/glib-2.0/include \ + /usr/include/libxml2 +LIBS += -lgstreamer-video \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lGLU \ + -lGL +} + +DEPENDPATH += . +MOC_DIR += ./GeneratedFiles/debug +OBJECTS_DIR += debug +UI_DIR += ./GeneratedFiles +RCC_DIR += ./GeneratedFiles + +#Include file(s) +include(mousevideooverlay.pri) diff --git a/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.sln b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.sln new file mode 100644 index 0000000..8d310d8 --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mousevideooverlay", "mousevideooverlay.vcproj", "{E94F8CAA-628F-4872-8E73-AD56D34899CB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E94F8CAA-628F-4872-8E73-AD56D34899CB}.Debug|Win32.ActiveCfg = Debug|Win32 + {E94F8CAA-628F-4872-8E73-AD56D34899CB}.Debug|Win32.Build.0 = Debug|Win32 + {E94F8CAA-628F-4872-8E73-AD56D34899CB}.Release|Win32.ActiveCfg = Release|Win32 + {E94F8CAA-628F-4872-8E73-AD56D34899CB}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(Qt) = preSolution + Integration = True + EndGlobalSection + GlobalSection(Qt) = preSolution + Integration = True + EndGlobalSection +EndGlobal diff --git a/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.vcproj b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.vcproj new file mode 100644 index 0000000..e4b7b18 --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/mousevideooverlay.vcproj @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/qt/mousevideooverlay/pipeline.cpp b/tests/examples/gl/qt/mousevideooverlay/pipeline.cpp new file mode 100644 index 0000000..9f8cc76 --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/pipeline.cpp @@ -0,0 +1,334 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#include "pipeline.h" + +Pipeline::Pipeline(const WId id, const QString videoLocation): + m_winId(id), + m_videoLocation(videoLocation), + m_loop(NULL), + m_bus(NULL), + m_pipeline(NULL), + m_glimagesink(NULL) +{ + create(); +} + +Pipeline::~Pipeline() +{ +} + +void Pipeline::create() +{ + qDebug("Loading video: %s", m_videoLocation.toAscii().data()); + + gst_init (NULL, NULL); + +#ifdef WIN32 + m_loop = g_main_loop_new (NULL, FALSE); +#endif + m_pipeline = gst_pipeline_new ("pipeline"); + + m_bus = gst_pipeline_get_bus (GST_PIPELINE (m_pipeline)); + gst_bus_add_watch (m_bus, (GstBusFunc) bus_call, this); + gst_bus_set_sync_handler (m_bus, (GstBusSyncHandler) create_window, this); + gst_object_unref (m_bus); + + GstElement* videosrc = gst_element_factory_make ("filesrc", "filesrc0"); + GstElement* decodebin = gst_element_factory_make ("decodebin", "decodebin0"); + m_glimagesink = gst_element_factory_make ("glimagesink", "sink0"); + + if (!videosrc || !decodebin || !m_glimagesink ) + { + qDebug ("one element could not be found"); + return; + } + + g_object_set(G_OBJECT(videosrc), "num-buffers", 800, NULL); + g_object_set(G_OBJECT(videosrc), "location", m_videoLocation.toAscii().data(), NULL); + g_object_set(G_OBJECT(m_glimagesink), "client-reshape-callback", reshapeCallback, NULL); + g_object_set(G_OBJECT(m_glimagesink), "client-draw-callback", drawCallback, NULL); + + gst_bin_add_many (GST_BIN (m_pipeline), videosrc, decodebin, m_glimagesink, NULL); + + gst_element_link_pads (videosrc, "src", decodebin, "sink"); + + g_signal_connect (decodebin, "new-decoded-pad", G_CALLBACK (cb_new_pad), this); +} + +void Pipeline::start() +{ + GstStateChangeReturn ret = gst_element_set_state (m_pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + qDebug ("Failed to start up pipeline!"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll (m_bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + gst_message_parse_error (msg, &err, NULL); + qDebug ("ERROR: %s", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return; + } + +#ifdef WIN32 + g_main_loop_run(m_loop); +#endif +} + +//we don't want a thread safe stop in this example +void Pipeline::stop() +{ +#ifdef WIN32 + g_main_loop_quit(m_loop); +#else + emit stopRequested(); +#endif +} + +void Pipeline::unconfigure() const +{ + gst_element_set_state (m_pipeline, GST_STATE_NULL); + gst_object_unref (m_pipeline); +} + +void Pipeline::show() +{ + emit showRequested(); +} + +//redraw the current frame in the drawable +void Pipeline::doExpose() const +{ + if (m_pipeline && m_glimagesink) + gst_video_overlay_expose (GST_VIDEO_OVERLAY (m_glimagesink)); +} + +//post message to g_main_loop in order to call expose +//in the gt thread +void Pipeline::exposeRequested() +{ + g_idle_add(cb_expose, this); +} + +//rotate the cube +void Pipeline::doRotate() +{ + m_xrot += 3.0f; + m_yrot += 2.0f; + m_zrot += 4.0f; +} + +//post message to g_main_loop in order to call rotate +//in the gt thread +void Pipeline::rotateRequested() +{ + g_idle_add(cb_rotate, this); +} + +//----------------------------------------------------------------------- +//----------------------------- static members -------------------------- +//----------------------------------------------------------------------- + +float Pipeline::m_xrot = 0; +float Pipeline::m_yrot = 0; +float Pipeline::m_zrot = 0; + +//client reshape callback +void Pipeline::reshapeCallback (uint width, uint height) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100); + glMatrixMode(GL_MODELVIEW); +} + +//client draw callback +gboolean Pipeline::drawCallback (uint texture, uint width, uint height) +{ + static GTimeVal current_time; + static glong last_sec = current_time.tv_sec; + static gint nbFrames = 0; + + g_get_current_time (¤t_time); + nbFrames++ ; + + if ((current_time.tv_sec - last_sec) >= 1) + { + qDebug ("GRPHIC FPS = %d", nbFrames); + nbFrames = 0; + last_sec = current_time.tv_sec; + } + + glEnable(GL_DEPTH_TEST); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(m_xrot,1.0f,0.0f,0.0f); + glRotatef(m_yrot,0.0f,1.0f,0.0f); + glRotatef(m_zrot,0.0f,0.0f,1.0f); + + glBegin(GL_QUADS); + // Front Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + //return TRUE causes a postRedisplay + return FALSE; +} + +gboolean Pipeline::bus_call (GstBus *bus, GstMessage *msg, Pipeline* p) +{ + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_EOS: + qDebug ("End-of-stream"); + p->stop(); + break; + case GST_MESSAGE_ERROR: + { + gchar *debug = NULL; + GError *err = NULL; + gst_message_parse_error (msg, &err, &debug); + qDebug ("Error: %s", err->message); + g_error_free (err); + if (debug) + { + qDebug ("Debug deails: %s", debug); + g_free (debug); + } + p->stop(); + break; + } + default: + break; + } + + return TRUE; +} + +void Pipeline::cb_new_pad (GstElement* decodebin, GstPad* pad, gboolean last, Pipeline* p) +{ + GstElement* glimagesink = p->getVideoSink(); + GstPad* glpad = gst_element_get_pad (glimagesink, "sink"); + + //only link once + if (GST_PAD_IS_LINKED (glpad)) + { + gst_object_unref (glpad); + return; + } + + GstCaps* caps = gst_pad_get_caps (pad); + GstStructure* str = gst_caps_get_structure (caps, 0); + if (!g_strrstr (gst_structure_get_name (str), "video")) + { + gst_caps_unref (caps); + gst_object_unref (glpad); + return; + } + gst_caps_unref (caps); + + GstPadLinkReturn ret = gst_pad_link (pad, glpad); + if (ret != GST_PAD_LINK_OK) + g_warning ("Failed to link with decodebin!\n"); + + p->show(); +} + +gboolean Pipeline::cb_expose (gpointer data) +{ + ((Pipeline*)data)->doExpose(); + return FALSE; +} + +gboolean Pipeline::cb_rotate (gpointer data) +{ + ((Pipeline*)data)->doRotate(); + return FALSE; +} + +GstBusSyncReply Pipeline::create_window (GstBus* bus, GstMessage* message, const Pipeline* p) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + qDebug ("setting window handle"); + + //Passing 0 as the window_handle will tell the overlay to stop using that window and create an internal one. + //In the directdrawsink's gst_video_overlay_set_window_handle implementation, window_handle (parameter 2) is casted to HWND before it used. + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), (guintptr)p->winId()); + + gst_message_unref (message); + + return GST_BUS_DROP; +} diff --git a/tests/examples/gl/qt/mousevideooverlay/pipeline.h b/tests/examples/gl/qt/mousevideooverlay/pipeline.h new file mode 100644 index 0000000..5cdadaf --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/pipeline.h @@ -0,0 +1,72 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#ifndef PIPELINE_H +#define PIPELINE_H + +#include +#include +//#include + +class Pipeline : public QObject +{ + Q_OBJECT + +public: + Pipeline(const WId windId, const QString videoLocation); + ~Pipeline(); + void start(); + void exposeRequested(); + void rotateRequested(); + void stop(); + void unconfigure() const; + void show(); + GstElement* getVideoSink() { return m_glimagesink; } ; + +signals: + void showRequested(); + void stopRequested(); + +private: + const WId m_winId; + const QString m_videoLocation; + GMainLoop* m_loop; + GstBus* m_bus; + GstElement* m_pipeline; + GstElement* m_glimagesink; + static float m_xrot; + static float m_yrot; + static float m_zrot; + + void create(); + WId winId() const { return m_winId; } + void doExpose() const; + void doRotate(); + + static void reshapeCallback (uint width, uint height); + static gboolean drawCallback (uint texture, uint width, uint height); + static gboolean bus_call (GstBus *bus, GstMessage *msg, Pipeline* p); + static void cb_new_pad (GstElement* decodebin, GstPad* pad, gboolean last, Pipeline* p); + static gboolean cb_expose (gpointer data); + static gboolean cb_rotate (gpointer data); + static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, const Pipeline* pipeline); +}; + +#endif diff --git a/tests/examples/gl/qt/mousevideooverlay/qrenderer.cpp b/tests/examples/gl/qt/mousevideooverlay/qrenderer.cpp new file mode 100644 index 0000000..54e94fc --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/qrenderer.cpp @@ -0,0 +1,58 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include "qrenderer.h" + +QRenderer::QRenderer(const QString videoLocation, QWidget *parent, Qt::WFlags flags) + : QWidget(parent, flags), + m_gt(winId(), videoLocation) +{ + setAttribute(Qt::WA_NoSystemBackground); + setVisible(false); + move(20, 10); + resize(640, 480); + + QObject::connect(&m_gt, SIGNAL(finished()), this, SLOT(close())); + QObject::connect(this, SIGNAL(exposeRequested()), &m_gt, SLOT(exposeRequested())); + QObject::connect(this, SIGNAL(closeRequested()), &m_gt, SLOT(stop()), Qt::DirectConnection); + QObject::connect(&m_gt, SIGNAL(showRequested()), this, SLOT(show())); + QObject::connect(this, SIGNAL(mouseMoved()), &m_gt, SLOT(onMouseMove())); + m_gt.start(); +} + +QRenderer::~QRenderer() +{ +} + +void QRenderer::paintEvent(QPaintEvent* event) +{ + emit exposeRequested(); +} + +void QRenderer::mouseMoveEvent(QMouseEvent* event) +{ + emit mouseMoved(); +} + +void QRenderer::closeEvent(QCloseEvent* event) +{ + emit closeRequested(); + m_gt.wait(); +} diff --git a/tests/examples/gl/qt/mousevideooverlay/qrenderer.h b/tests/examples/gl/qt/mousevideooverlay/qrenderer.h new file mode 100644 index 0000000..771876d --- /dev/null +++ b/tests/examples/gl/qt/mousevideooverlay/qrenderer.h @@ -0,0 +1,48 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#ifndef QRENDERER_H +#define QRENDERER_H + +#include +#include +#include "gstthread.h" + +class QRenderer : public QWidget +{ + Q_OBJECT + +public: + QRenderer(const QString videoLocation, QWidget *parent = 0, Qt::WFlags flags = 0); + ~QRenderer(); + void paintEvent(QPaintEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void closeEvent (QCloseEvent* event); + +signals: + void exposeRequested(); + void closeRequested(); + void mouseMoved(); + +private: + GstThread m_gt; +}; + +#endif // QRENDERER_H diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/gstthread.cpp b/tests/examples/gl/qt/qglwidgetvideooverlay/gstthread.cpp new file mode 100644 index 0000000..a31f8d6 --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/gstthread.cpp @@ -0,0 +1,62 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include "gstthread.h" + +GstThread::GstThread(const WId winId, const QString videoLocation, QObject *parent): + QThread(parent), + m_winId(winId), + m_videoLocation(videoLocation) +{ +} + +GstThread::~GstThread() +{ +} + +void GstThread::exposeRequested() +{ + m_pipeline->exposeRequested(); +} + +void GstThread::show() +{ + emit showRequested(); +} + +void GstThread::stop() +{ + m_pipeline->stop(); +} + +void GstThread::run() +{ + m_pipeline = new Pipeline(m_winId, m_videoLocation); + connect(m_pipeline, SIGNAL(showRequested()), this, SLOT(show())); + m_pipeline->start(); //it runs the gmainloop on win32 + +#ifndef WIN32 + //works like the gmainloop on linux (GstEvent are handled) + connect(m_pipeline, SIGNAL(stopRequested()), this, SLOT(quit())); + exec(); +#endif + + m_pipeline->unconfigure(); +} diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/gstthread.h b/tests/examples/gl/qt/qglwidgetvideooverlay/gstthread.h new file mode 100644 index 0000000..676b987 --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/gstthread.h @@ -0,0 +1,55 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#ifndef GSTTHREAD_H +#define GSTTHREAD_H + +#include +#include + +#include "pipeline.h" + +class GstThread : public QThread +{ + Q_OBJECT + +public: + GstThread(const WId winId, const QString videoLocation, QObject *parent = 0); + ~GstThread(); + +public slots: + void exposeRequested(); + void show(); + void stop(); + +signals: + void showRequested(); + +protected: + void run(); + +private: + const WId m_winId; + const QString m_videoLocation; + Pipeline* m_pipeline; + +}; + +#endif diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/main.cpp b/tests/examples/gl/qt/qglwidgetvideooverlay/main.cpp new file mode 100644 index 0000000..fa54a6b --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/main.cpp @@ -0,0 +1,39 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include "qglrenderer.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit())); + + QString videolcoation = QFileDialog::getOpenFileName(0, "Select a video file", + ".", "Format (*.avi *.mkv *.ogg *.asf *.mov)"); + + if (videolcoation.isEmpty()) + return -1; + + QGLRenderer w(videolcoation); + w.setWindowTitle("glimagesink implements the gstvideooverlay interface"); + + return a.exec(); +} diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/pipeline.cpp b/tests/examples/gl/qt/qglwidgetvideooverlay/pipeline.cpp new file mode 100644 index 0000000..617524a --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/pipeline.cpp @@ -0,0 +1,332 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include +#include +#include "pipeline.h" + +Pipeline::Pipeline(const WId id, const QString videoLocation): + m_winId(id), + m_videoLocation(videoLocation), + m_loop(NULL), + m_bus(NULL), + m_pipeline(NULL), + m_glupload(NULL), + m_glimagesink(NULL) +{ + create(); +} + +Pipeline::~Pipeline() +{ +} + +void Pipeline::create() +{ + qDebug("Loading video: %s", m_videoLocation.toAscii().data()); + + gst_init (NULL, NULL); + +#ifdef WIN32 + m_loop = g_main_loop_new (NULL, FALSE); +#endif + m_pipeline = gst_pipeline_new ("pipeline"); + + m_bus = gst_pipeline_get_bus (GST_PIPELINE (m_pipeline)); + gst_bus_add_watch (m_bus, (GstBusFunc) bus_call, this); + gst_bus_set_sync_handler (m_bus, (GstBusSyncHandler) create_window, this); + gst_object_unref (m_bus); + + GstElement* videosrc = gst_element_factory_make ("filesrc", "filesrc0"); + GstElement* decodebin = gst_element_factory_make ("decodebin", "decodebin0"); + m_glupload = gst_element_factory_make ("glupload", "glupload0"); + m_glimagesink = gst_element_factory_make ("glimagesink", "sink0"); + + if (!videosrc || !decodebin || !m_glupload || !m_glimagesink ) + { + qDebug ("one element could not be found"); + return; + } + + GstCaps *outcaps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 800, + "height", G_TYPE_INT, 600, + NULL) ; + + g_object_set(G_OBJECT(videosrc), "num-buffers", 800, NULL); + g_object_set(G_OBJECT(videosrc), "location", m_videoLocation.toAscii().data(), NULL); + g_object_set(G_OBJECT(m_glimagesink), "client-reshape-callback", reshapeCallback, NULL); + g_object_set(G_OBJECT(m_glimagesink), "client-draw-callback", drawCallback, NULL); + + gst_bin_add_many (GST_BIN (m_pipeline), videosrc, decodebin, m_glupload, m_glimagesink, NULL); + + gboolean link_ok = gst_element_link_filtered(m_glupload, m_glimagesink, outcaps) ; + gst_caps_unref(outcaps) ; + if(!link_ok) + { + qDebug("Failed to link glupload to glimagesink!\n") ; + return; + } + + gst_element_link_pads (videosrc, "src", decodebin, "sink"); + + g_signal_connect (decodebin, "new-decoded-pad", G_CALLBACK (cb_new_pad), this); +} + +void Pipeline::start() +{ + GstStateChangeReturn ret = gst_element_set_state (m_pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + qDebug ("Failed to start up pipeline!"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll (m_bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + gst_message_parse_error (msg, &err, NULL); + qDebug ("ERROR: %s", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return; + } + +#ifdef WIN32 + g_main_loop_run(m_loop); +#endif +} + +//we don't want a thread safe stop in this example +void Pipeline::stop() +{ +#ifdef WIN32 + g_main_loop_quit(m_loop); +#else + emit stopRequested(); +#endif +} + +void Pipeline::unconfigure() const +{ + gst_element_set_state (m_pipeline, GST_STATE_NULL); + gst_object_unref (m_pipeline); +} + +void Pipeline::show() +{ + emit showRequested(); +} + +//redraw the current frame in the drawable +void Pipeline::doExpose() const +{ + if (m_pipeline && m_glimagesink) + gst_video_overlay_expose (GST_VIDEO_OVERLAY (m_glimagesink)); +} + +//post message to g_main_loop in order to call expose +//in the gt thread +void Pipeline::exposeRequested() +{ + g_idle_add(cb_expose, this); +} + + +//----------------------------------------------------------------------- +//----------------------------- static members -------------------------- +//----------------------------------------------------------------------- + +//client reshape callback +void Pipeline::reshapeCallback (uint width, uint height) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (gfloat)width/(gfloat)height, 0.1, 100); + glMatrixMode(GL_MODELVIEW); +} + +//client draw callback +gboolean Pipeline::drawCallback (uint texture, uint width, uint height) +{ + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + static GTimeVal current_time; + static glong last_sec = current_time.tv_sec; + static gint nbFrames = 0; + + g_get_current_time (¤t_time); + nbFrames++ ; + + if ((current_time.tv_sec - last_sec) >= 1) + { + qDebug ("GRPHIC FPS = %d", nbFrames); + nbFrames = 0; + last_sec = current_time.tv_sec; + } + + glEnable(GL_DEPTH_TEST); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(xrot,1.0f,0.0f,0.0f); + glRotatef(yrot,0.0f,1.0f,0.0f); + glRotatef(zrot,0.0f,0.0f,1.0f); + + glBegin(GL_QUADS); + // Front Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f((gfloat)width,(gfloat)height); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f((gfloat)width, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, (gfloat)height); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f((gfloat)width, (gfloat)height); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot+=0.03f; + yrot+=0.02f; + zrot+=0.04f; + + //return TRUE causes a postRedisplay + return TRUE; +} + +gboolean Pipeline::bus_call (GstBus *bus, GstMessage *msg, Pipeline* p) +{ + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_EOS: + qDebug ("End-of-stream"); + p->stop(); + break; + case GST_MESSAGE_ERROR: + { + gchar *debug = NULL; + GError *err = NULL; + gst_message_parse_error (msg, &err, &debug); + qDebug ("Error: %s", err->message); + g_error_free (err); + if (debug) + { + qDebug ("Debug deails: %s", debug); + g_free (debug); + } + p->stop(); + break; + } + default: + break; + } + + return TRUE; +} + +void Pipeline::cb_new_pad (GstElement* decodebin, GstPad* pad, gboolean last, Pipeline* p) +{ + GstElement* glupload = p->getVideoSink(); + GstPad* glpad = gst_element_get_pad (glupload, "sink"); + + //only link once + if (GST_PAD_IS_LINKED (glpad)) + { + gst_object_unref (glpad); + return; + } + + GstCaps* caps = gst_pad_get_caps (pad); + GstStructure* str = gst_caps_get_structure (caps, 0); + if (!g_strrstr (gst_structure_get_name (str), "video")) + { + gst_caps_unref (caps); + gst_object_unref (glpad); + return; + } + gst_caps_unref (caps); + + GstPadLinkReturn ret = gst_pad_link (pad, glpad); + if (ret != GST_PAD_LINK_OK) + g_warning ("Failed to link with decodebin!\n"); + + p->show(); +} + +gboolean Pipeline::cb_expose (gpointer data) +{ + ((Pipeline*)data)->doExpose(); + return FALSE; +} + +GstBusSyncReply Pipeline::create_window (GstBus* bus, GstMessage* message, const Pipeline* p) +{ + // ignore anything but 'prepare-window-handle' element messages + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + qDebug ("setting window handle"); + + //Passing 0 as the window_handle will tell the overlay to stop using that window and create an internal one. + //In the directdrawsink's gst_video_overlay_set_window_handle implementation, window_handle (parameter 2) is casted to HWND before it used. + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), (guintptr)p->winId()); + + gst_message_unref (message); + + return GST_BUS_DROP; +} diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/pipeline.h b/tests/examples/gl/qt/qglwidgetvideooverlay/pipeline.h new file mode 100644 index 0000000..51c48fc --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/pipeline.h @@ -0,0 +1,70 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#ifndef PIPELINE_H +#define PIPELINE_H + +#include +#include +//#include + +class Pipeline : public QObject +{ + Q_OBJECT + +public: + Pipeline(const WId windId, const QString videoLocation); + ~Pipeline(); + void start(); + void exposeRequested(); + void stop(); + void unconfigure() const; + void show(); + GstElement* getVideoSink() { return m_glupload; } ; + +signals: + void showRequested(); + void stopRequested(); + +private: + const WId m_winId; + const QString m_videoLocation; + GMainLoop* m_loop; + GstBus* m_bus; + GstElement* m_pipeline; + GstElement* m_glupload; + GstElement* m_glimagesink; + static float m_xrot; + static float m_yrot; + static float m_zrot; + + void create(); + WId winId() const { return m_winId; } + void doExpose() const; + + static void reshapeCallback (uint width, uint height); + static gboolean drawCallback (uint texture, uint width, uint height); + static gboolean bus_call (GstBus *bus, GstMessage *msg, Pipeline* p); + static void cb_new_pad (GstElement* decodebin, GstPad* pad, gboolean last, Pipeline* p); + static gboolean cb_expose (gpointer data); + static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, const Pipeline* pipeline); +}; + +#endif diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/qglrenderer.cpp b/tests/examples/gl/qt/qglwidgetvideooverlay/qglrenderer.cpp new file mode 100644 index 0000000..a54625e --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/qglrenderer.cpp @@ -0,0 +1,52 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#include "qglrenderer.h" + +QGLRenderer::QGLRenderer(const QString videoLocation, QWidget *parent) + : QGLWidget(parent), + m_gt(winId(), videoLocation) +{ + setAttribute(Qt::WA_NoSystemBackground); + setVisible(false); + move(20, 10); + resize(640, 480); + + QObject::connect(&m_gt, SIGNAL(finished()), this, SLOT(close())); + QObject::connect(this, SIGNAL(exposeRequested()), &m_gt, SLOT(exposeRequested())); + QObject::connect(this, SIGNAL(closeRequested()), &m_gt, SLOT(stop()), Qt::DirectConnection); + QObject::connect(&m_gt, SIGNAL(showRequested()), this, SLOT(show())); + m_gt.start(); +} + +QGLRenderer::~QGLRenderer() +{ +} + +void QGLRenderer::paintEvent(QPaintEvent* event) +{ + emit exposeRequested(); +} + +void QGLRenderer::closeEvent(QCloseEvent* event) +{ + emit closeRequested(); + m_gt.wait(); +} diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/qglrenderer.h b/tests/examples/gl/qt/qglwidgetvideooverlay/qglrenderer.h new file mode 100644 index 0000000..aaf0d02 --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/qglrenderer.h @@ -0,0 +1,45 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * + * 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. + */ + +#ifndef QGLRENDERER_H +#define QGLRENDERER_H + +#include +#include "gstthread.h" + +class QGLRenderer : public QGLWidget +{ + Q_OBJECT + +public: + QGLRenderer(const QString videoLocation, QWidget *parent = 0); + ~QGLRenderer(); + void paintEvent(QPaintEvent* event); + void closeEvent (QCloseEvent* event); + +signals: + void exposeRequested(); + void closeRequested(); + +private: + GstThread m_gt; +}; + +#endif // QGLRENDERER_H diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.pri b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.pri new file mode 100644 index 0000000..772d1d4 --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.pri @@ -0,0 +1,10 @@ +#Header files +HEADERS += ./gstthread.h \ + ./pipeline.h \ + ./qglrenderer.h + +#Source files +SOURCES += ./gstthread.cpp \ + ./main.cpp \ + ./pipeline.cpp \ + ./qglrenderer.cpp diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.pro b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.pro new file mode 100644 index 0000000..2fa56e8 --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.pro @@ -0,0 +1,55 @@ +TEMPLATE = app +TARGET = qglwidgetvideooverlay +DESTDIR = ./debug +QT += opengl +CONFIG += debug +DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB + +win32 { +DEFINES += WIN32 +INCLUDEPATH += ./GeneratedFiles \ + ./GeneratedFiles/Debug \ + C:/gstreamer/include \ + C:/gstreamer/include/libxml2 \ + C:/gstreamer/include/glib-2.0 \ + C:/gstreamer/lib/glib-2.0/include \ + C:/gstreamer/include/gstreamer-1.0 +LIBS += -L"C:/gstreamer/lib" \ + -L"C:/gstreamer/bin" \ + -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lopengl32 \ + -lglu32 +} + +unix { +DEFINES += UNIX +INCLUDEPATH += GeneratedFiles \ + GeneratedFiles/Debug \ + /usr/include/gstreamer-1.0 \ + /usr/local/include/gstreamer-1.0 \ + /usr/include/glib-2.0 \ + /usr/lib/glib-2.0/include \ + /usr/include/libxml2 +LIBS += -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lGLU \ + -lGL +} + +DEPENDPATH += . +MOC_DIR += ./GeneratedFiles/debug +OBJECTS_DIR += debug +UI_DIR += ./GeneratedFiles +RCC_DIR += ./GeneratedFiles + +#Include file(s) +include(qglwidgetvideooverlay.pri) diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.sln b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.sln new file mode 100644 index 0000000..3842be0 --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qglwidgetvideooverlay", "qglwidgetvideooverlay.vcproj", "{F40D2D98-281E-465F-B63F-2D473E1C2616}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F40D2D98-281E-465F-B63F-2D473E1C2616}.Debug|Win32.ActiveCfg = Debug|Win32 + {F40D2D98-281E-465F-B63F-2D473E1C2616}.Debug|Win32.Build.0 = Debug|Win32 + {F40D2D98-281E-465F-B63F-2D473E1C2616}.Release|Win32.ActiveCfg = Release|Win32 + {F40D2D98-281E-465F-B63F-2D473E1C2616}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(Qt) = preSolution + Integration = True + EndGlobalSection + GlobalSection(Qt) = preSolution + Integration = True + EndGlobalSection +EndGlobal diff --git a/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.vcproj b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.vcproj new file mode 100644 index 0000000..4abca4b --- /dev/null +++ b/tests/examples/gl/qt/qglwidgetvideooverlay/qglwidgetvideooverlay.vcproj @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/qt/qglwtextureshare/AsyncQueue.h b/tests/examples/gl/qt/qglwtextureshare/AsyncQueue.h new file mode 100644 index 0000000..4582bcb --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/AsyncQueue.h @@ -0,0 +1,77 @@ +/* + * GStreamer + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#ifndef __ASYNCQUEUE_H +#define __ASYNCQUEUE_H + +#include +#include +#include + +/** + * This is the thread safe implementation of the Queue. It can be + * used in classical producers/consumers multithreaded scenario. The + * template parameter is the class which can be put/get to/from the + * queue. + */ +template +class AsyncQueue +{ +public: + AsyncQueue() : waitingReaders(0) {} + + int size() + { + QMutexLocker locker(&mutex); + return this->buffer.size(); + } + + void put(const T& item) + { + QMutexLocker locker(&mutex); + this->buffer.push_back(item); + if(this->waitingReaders) + this->bufferIsNotEmpty.wakeOne(); + } + + T get() + { + QMutexLocker locker(&mutex); + while(this->buffer.size() == 0) + { + ++(this->waitingReaders); + this->bufferIsNotEmpty.wait(&mutex); + --(this->waitingReaders); + } + T item = this->buffer.front(); + this->buffer.pop_front(); + return item; + } + +private: + typedef QList Container; + QMutex mutex; + QWaitCondition bufferIsNotEmpty; + Container buffer; + short waitingReaders; +}; + + +#endif // __ASYNCQUEUE_H diff --git a/tests/examples/gl/qt/qglwtextureshare/README b/tests/examples/gl/qt/qglwtextureshare/README new file mode 100644 index 0000000..305847c --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/README @@ -0,0 +1,29 @@ +This example illustrates how to integrate Gstreamer GL plugin with +Qt. In particular it uses glupload with fakesink elements to create +texture with decoded video frame. This texture is shared with +QGLWidget derived class, which paints a cube with video texture on +each face. + +To compile the example, include and library paths might be adjusted in +.pro file according to your installation of the gstreamer and +corresponding development files. Most probably, the adjustments will +be necessary on Windows. + +To run the example simply start executable file after compilation. If +there is no command line arguments provided, then videotestsrc element +will be used to generate video. The following pipeline will be created +in this case: + +videotestsrc ! video/x-raw, width=640, height=480, framerate=(fraction)30/1 ! glupload ! fakesink sync=1 + +It is also possible to provide the video file name as a first command +line parameter, i.e. ./qglwtextureshare myvideo.ogv . In this case, +the following pipeline will be executed: + +filesrc location=myvideo.ogv ! decodebin2 ! glupload ! fakesink sync=1 + +I would appreciate any feedback and improvement suggestions for this +example. + +Have fun :-) +Andrey Nechypurenko (andreynech@gmail.com) diff --git a/tests/examples/gl/qt/qglwtextureshare/cocoa_utils.mm b/tests/examples/gl/qt/qglwtextureshare/cocoa_utils.mm new file mode 100644 index 0000000..9b1e78b --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/cocoa_utils.mm @@ -0,0 +1,26 @@ +/* + * GStreamer + * Copyright (C) 2010 Julien Isorce + * Copyright (C) 2010 Nuno Santos + * + * 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. + */ + +#import +void *qt_current_nsopengl_context() +{ + return [NSOpenGLContext currentContext]; +} diff --git a/tests/examples/gl/qt/qglwtextureshare/glcontextid.h b/tests/examples/gl/qt/qglwtextureshare/glcontextid.h new file mode 100644 index 0000000..c6a240c --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/glcontextid.h @@ -0,0 +1,65 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * Copyright (C) 2010 Nuno Santos + * + * 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. + */ + +#ifndef __GLCONTEXTID_H +#define __GLCONTEXTID_H + +#include + +#if defined(GST_GL_HAVE_PLATFORM_WGL) + #define WIN32_LEAN_AND_MEAN + #include + #include + #include +#elif defined (GST_GL_HAVE_PLATFORM_COCOA) + #include + class NSOpenGLContext; +#else + #include + #include + #include + #include +#endif + + +#if defined(GST_GL_HAVE_PLATFORM_WGL) + typedef struct _tagGLContextID + { + HGLRC contextId; + HDC dc; + } GLContextID; +#elif defined(GST_GL_HAVE_PLATFORM_COCOA) + typedef struct _tagGLContextID + { + NSOpenGLContext* contextId; + } GLContextID; +#elif defined(GST_GL_HAVE_PLATFORM_GLX) + typedef struct _tagGLContextID + { + GLXContext contextId; + Display *display; + Window wnd; + } GLContextID; +#endif + +#endif // __GLCONTEXTID_H + diff --git a/tests/examples/gl/qt/qglwtextureshare/gstthread.cpp b/tests/examples/gl/qt/qglwtextureshare/gstthread.cpp new file mode 100644 index 0000000..2a1ff69 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/gstthread.cpp @@ -0,0 +1,66 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#include "pipeline.h" +#include "gstthread.h" + + +GstThread::GstThread(GstGLContext *context, + const QString &videoLocation, + const char *renderer_slot, + QObject *parent): + QThread(parent), + m_videoLocation(videoLocation) +{ + this->context = context; + m_pipeline = new Pipeline(this->context, m_videoLocation, this); + QObject::connect(m_pipeline, SIGNAL(newFrameReady()), this->parent(), renderer_slot, Qt::QueuedConnection); +} + +GstThread::~GstThread() +{ +} + +void GstThread::stop() +{ + if(m_pipeline) + m_pipeline->stop(); +} + +void GstThread::run() +{ + qDebug("Starting gst pipeline"); + m_pipeline->start(); //it runs the gmainloop on win32 + +#ifndef Q_WS_WIN + //works like the gmainloop on linux (GstEvent are handled) + connect(m_pipeline, SIGNAL(stopRequested()), this, SLOT(quit())); + exec(); +#endif + + m_pipeline->unconfigure(); + + m_pipeline = NULL; + // This is not a memory leak. Pipeline will be deleted + // when the parent object (this) will be destroyed. + // We set m_pipeline to NULL to prevent further attempts + // to stop already stopped pipeline +} diff --git a/tests/examples/gl/qt/qglwtextureshare/gstthread.h b/tests/examples/gl/qt/qglwtextureshare/gstthread.h new file mode 100644 index 0000000..918859e --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/gstthread.h @@ -0,0 +1,59 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#ifndef GSTTHREAD_H +#define GSTTHREAD_H + +#include + +#include + +#include "glcontextid.h" + +class Pipeline; + +class GstThread : public QThread +{ + Q_OBJECT + +public: + GstThread(GstGLContext *context, + const QString &videoLocation, + const char *renderer_slot, + QObject *parent = 0); + + ~GstThread(); + + Pipeline *getPipeline() {return this->m_pipeline;} + +public Q_SLOTS: + void stop(); + +protected: + void run(); + +private: + GstGLContext *context; + const QString m_videoLocation; + Pipeline* m_pipeline; +}; + +#endif diff --git a/tests/examples/gl/qt/qglwtextureshare/main.cpp b/tests/examples/gl/qt/qglwtextureshare/main.cpp new file mode 100644 index 0000000..f2eb2f1 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/main.cpp @@ -0,0 +1,36 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#include +#include "qglrenderer.h" + + +int +main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit())); + QGLRenderer w(argc > 1 ? argv[1] : ""); + w.setWindowTitle("Texture sharing example"); + w.show(); + return a.exec(); +} + diff --git a/tests/examples/gl/qt/qglwtextureshare/moc_gstthread.cpp b/tests/examples/gl/qt/qglwtextureshare/moc_gstthread.cpp new file mode 100644 index 0000000..1787e38 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/moc_gstthread.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'gstthread.h' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "gstthread.h" +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'gstthread.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.1. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_GstThread_t { + QByteArrayData data[3]; + char stringdata[17]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_GstThread_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_GstThread_t qt_meta_stringdata_GstThread = { + { +QT_MOC_LITERAL(0, 0, 9), +QT_MOC_LITERAL(1, 10, 4), +QT_MOC_LITERAL(2, 15, 0) + }, + "GstThread\0stop\0\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_GstThread[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: name, argc, parameters, tag, flags + 1, 0, 19, 2, 0x0a, + + // slots: parameters + QMetaType::Void, + + 0 // eod +}; + +void GstThread::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + GstThread *_t = static_cast(_o); + switch (_id) { + case 0: _t->stop(); break; + default: ; + } + } + Q_UNUSED(_a); +} + +const QMetaObject GstThread::staticMetaObject = { + { &QThread::staticMetaObject, qt_meta_stringdata_GstThread.data, + qt_meta_data_GstThread, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *GstThread::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *GstThread::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_GstThread.stringdata)) + return static_cast(const_cast< GstThread*>(this)); + return QThread::qt_metacast(_clname); +} + +int GstThread::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QThread::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 1) + *reinterpret_cast(_a[0]) = -1; + _id -= 1; + } + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/tests/examples/gl/qt/qglwtextureshare/moc_pipeline.cpp b/tests/examples/gl/qt/qglwtextureshare/moc_pipeline.cpp new file mode 100644 index 0000000..fdf0cf7 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/moc_pipeline.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'pipeline.h' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "pipeline.h" +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'pipeline.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.1. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_Pipeline_t { + QByteArrayData data[4]; + char stringdata[39]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_Pipeline_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_Pipeline_t qt_meta_stringdata_Pipeline = { + { +QT_MOC_LITERAL(0, 0, 8), +QT_MOC_LITERAL(1, 9, 13), +QT_MOC_LITERAL(2, 23, 0), +QT_MOC_LITERAL(3, 24, 13) + }, + "Pipeline\0newFrameReady\0\0stopRequested\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_Pipeline[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 2, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 2, // signalCount + + // signals: name, argc, parameters, tag, flags + 1, 0, 24, 2, 0x06, + 3, 0, 25, 2, 0x06, + + // signals: parameters + QMetaType::Void, + QMetaType::Void, + + 0 // eod +}; + +void Pipeline::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Pipeline *_t = static_cast(_o); + switch (_id) { + case 0: _t->newFrameReady(); break; + case 1: _t->stopRequested(); break; + default: ; + } + } else if (_c == QMetaObject::IndexOfMethod) { + int *result = reinterpret_cast(_a[0]); + void **func = reinterpret_cast(_a[1]); + { + typedef void (Pipeline::*_t)(); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Pipeline::newFrameReady)) { + *result = 0; + } + } + { + typedef void (Pipeline::*_t)(); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Pipeline::stopRequested)) { + *result = 1; + } + } + } + Q_UNUSED(_a); +} + +const QMetaObject Pipeline::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_Pipeline.data, + qt_meta_data_Pipeline, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *Pipeline::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *Pipeline::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Pipeline.stringdata)) + return static_cast(const_cast< Pipeline*>(this)); + return QObject::qt_metacast(_clname); +} + +int Pipeline::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 2) + qt_static_metacall(this, _c, _id, _a); + _id -= 2; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 2) + *reinterpret_cast(_a[0]) = -1; + _id -= 2; + } + return _id; +} + +// SIGNAL 0 +void Pipeline::newFrameReady() +{ + QMetaObject::activate(this, &staticMetaObject, 0, 0); +} + +// SIGNAL 1 +void Pipeline::stopRequested() +{ + QMetaObject::activate(this, &staticMetaObject, 1, 0); +} +QT_END_MOC_NAMESPACE diff --git a/tests/examples/gl/qt/qglwtextureshare/moc_qglrenderer.cpp b/tests/examples/gl/qt/qglwtextureshare/moc_qglrenderer.cpp new file mode 100644 index 0000000..5a11b71 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/moc_qglrenderer.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'qglrenderer.h' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "qglrenderer.h" +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'qglrenderer.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.1. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_QGLRenderer_t { + QByteArrayData data[4]; + char stringdata[38]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_QGLRenderer_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_QGLRenderer_t qt_meta_stringdata_QGLRenderer = { + { +QT_MOC_LITERAL(0, 0, 11), +QT_MOC_LITERAL(1, 12, 14), +QT_MOC_LITERAL(2, 27, 0), +QT_MOC_LITERAL(3, 28, 8) + }, + "QGLRenderer\0closeRequested\0\0newFrame\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_QGLRenderer[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 2, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 1, // signalCount + + // signals: name, argc, parameters, tag, flags + 1, 0, 24, 2, 0x06, + + // slots: name, argc, parameters, tag, flags + 3, 0, 25, 2, 0x0a, + + // signals: parameters + QMetaType::Void, + + // slots: parameters + QMetaType::Void, + + 0 // eod +}; + +void QGLRenderer::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + QGLRenderer *_t = static_cast(_o); + switch (_id) { + case 0: _t->closeRequested(); break; + case 1: _t->newFrame(); break; + default: ; + } + } else if (_c == QMetaObject::IndexOfMethod) { + int *result = reinterpret_cast(_a[0]); + void **func = reinterpret_cast(_a[1]); + { + typedef void (QGLRenderer::*_t)(); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&QGLRenderer::closeRequested)) { + *result = 0; + } + } + } + Q_UNUSED(_a); +} + +const QMetaObject QGLRenderer::staticMetaObject = { + { &QGLWidget::staticMetaObject, qt_meta_stringdata_QGLRenderer.data, + qt_meta_data_QGLRenderer, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *QGLRenderer::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *QGLRenderer::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QGLRenderer.stringdata)) + return static_cast(const_cast< QGLRenderer*>(this)); + return QGLWidget::qt_metacast(_clname); +} + +int QGLRenderer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QGLWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 2) + qt_static_metacall(this, _c, _id, _a); + _id -= 2; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 2) + *reinterpret_cast(_a[0]) = -1; + _id -= 2; + } + return _id; +} + +// SIGNAL 0 +void QGLRenderer::closeRequested() +{ + QMetaObject::activate(this, &staticMetaObject, 0, 0); +} +QT_END_MOC_NAMESPACE diff --git a/tests/examples/gl/qt/qglwtextureshare/pipeline.cpp b/tests/examples/gl/qt/qglwtextureshare/pipeline.cpp new file mode 100644 index 0000000..a7dd490 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/pipeline.cpp @@ -0,0 +1,222 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#include "pipeline.h" + + +Pipeline::Pipeline(GstGLContext *context, + const QString &videoLocation, + QObject *parent) + : QObject(parent), + m_videoLocation(videoLocation), + m_loop(NULL), + m_bus(NULL), + m_pipeline(NULL) +{ + this->context = context; + this->configure(); +} + +Pipeline::~Pipeline() +{ +} + +void +Pipeline::configure() +{ + gst_init (NULL, NULL); + +#ifdef Q_WS_WIN + m_loop = g_main_loop_new (NULL, FALSE); +#endif + + if(m_videoLocation.isEmpty()) + { + qDebug("No video file specified. Using video test source."); + m_pipeline = + GST_PIPELINE (gst_parse_launch + ("videotestsrc ! " + "video/x-raw, width=640, height=480, " + "framerate=(fraction)30/1 ! " + "gleffects effect=5 ! fakesink sync=1", + NULL)); + } + else + { + QByteArray ba = m_videoLocation.toLocal8Bit(); + qDebug("Loading video: %s", ba.data()); + gchar *pipeline = g_strdup_printf ("filesrc location='%s' ! " + "decodebin ! gleffects effect=5 ! " + "fakesink sync=1", ba.data()); + m_pipeline = GST_PIPELINE (gst_parse_launch (pipeline, NULL)); + g_free (pipeline); + } + + m_bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline)); + gst_bus_add_watch(m_bus, (GstBusFunc) bus_call, this); + gst_object_unref(m_bus); + + /* Retrieve the last gl element */ + GstElement *gl_element = gst_bin_get_by_name(GST_BIN(m_pipeline), "gleffects0"); + if(!gl_element) + { + qDebug ("gl element could not be found"); + return; + } + g_object_set(G_OBJECT (gl_element), "other-context", + this->context, NULL); + gst_object_unref(gl_element); + + gst_element_set_state(GST_ELEMENT(this->m_pipeline), GST_STATE_PAUSED); + GstState state = GST_STATE_PAUSED; + if(gst_element_get_state(GST_ELEMENT(this->m_pipeline), + &state, NULL, GST_CLOCK_TIME_NONE) + != GST_STATE_CHANGE_SUCCESS) + { + qDebug("failed to pause pipeline"); + return; + } +} + +void +Pipeline::start() +{ + // set a callback to retrieve the gst gl textures + GstElement *fakesink = gst_bin_get_by_name(GST_BIN(this->m_pipeline), + "fakesink0"); + g_object_set(G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL); + g_signal_connect(fakesink, "handoff", G_CALLBACK (on_gst_buffer), this); + gst_object_unref(fakesink); + + GstStateChangeReturn ret = + gst_element_set_state(GST_ELEMENT(this->m_pipeline), GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + qDebug("Failed to start up pipeline!"); + + /* check if there is an error message with details on the bus */ + GstMessage* msg = gst_bus_poll(this->m_bus, GST_MESSAGE_ERROR, 0); + if (msg) + { + GError *err = NULL; + gst_message_parse_error (msg, &err, NULL); + qDebug ("ERROR: %s", err->message); + g_error_free (err); + gst_message_unref (msg); + } + return; + } + +#ifdef Q_WS_WIN + g_main_loop_run(m_loop); +#endif +} + +/* fakesink handoff callback */ +void +Pipeline::on_gst_buffer(GstElement * element, + GstBuffer * buf, + GstPad * pad, + Pipeline* p) +{ + Q_UNUSED(pad) + Q_UNUSED(element) + + /* ref then push buffer to use it in qt */ + gst_buffer_ref(buf); + p->queue_input_buf.put(buf); + + if (p->queue_input_buf.size() > 3) + p->notifyNewFrame(); + + /* pop then unref buffer we have finished to use in qt */ + if (p->queue_output_buf.size() > 3) + { + GstBuffer *buf_old = (p->queue_output_buf.get()); + if (buf_old) + gst_buffer_unref(buf_old); + } +} + +void +Pipeline::stop() +{ +#ifdef Q_WS_WIN + g_main_loop_quit(m_loop); +#else + emit stopRequested(); +#endif +} + +void +Pipeline::unconfigure() +{ + gst_element_set_state(GST_ELEMENT(this->m_pipeline), GST_STATE_NULL); + + GstBuffer *buf; + while(this->queue_input_buf.size()) + { + buf = (GstBuffer*)(this->queue_input_buf.get()); + gst_buffer_unref(buf); + } + while(this->queue_output_buf.size()) + { + buf = (GstBuffer*)(this->queue_output_buf.get()); + gst_buffer_unref(buf); + } + + gst_object_unref(m_pipeline); +} + +gboolean +Pipeline::bus_call(GstBus *bus, GstMessage *msg, Pipeline* p) +{ + Q_UNUSED(bus) + + switch(GST_MESSAGE_TYPE(msg)) + { + case GST_MESSAGE_EOS: + qDebug("End-of-stream received. Stopping."); + p->stop(); + break; + + case GST_MESSAGE_ERROR: + { + gchar *debug = NULL; + GError *err = NULL; + gst_message_parse_error(msg, &err, &debug); + qDebug("Error: %s", err->message); + g_error_free (err); + if(debug) + { + qDebug("Debug deails: %s", debug); + g_free(debug); + } + p->stop(); + break; + } + + default: + break; + } + + return TRUE; +} diff --git a/tests/examples/gl/qt/qglwtextureshare/pipeline.h b/tests/examples/gl/qt/qglwtextureshare/pipeline.h new file mode 100644 index 0000000..5e59f1a --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/pipeline.h @@ -0,0 +1,70 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#ifndef PIPELINE_H +#define PIPELINE_H + +#include + +#include + +#include "glcontextid.h" +#include "AsyncQueue.h" + + +class Pipeline : public QObject +{ + Q_OBJECT + +public: + Pipeline(GstGLContext *context, + const QString &videoLocation, + QObject *parent); + ~Pipeline(); + + void configure(); + void start(); + void notifyNewFrame() {emit newFrameReady();} + void stop(); + void unconfigure(); + + AsyncQueue queue_input_buf; + AsyncQueue queue_output_buf; + +Q_SIGNALS: + void newFrameReady(); + void stopRequested(); + +private: + GstGLContext *context; + const QString m_videoLocation; + GMainLoop* m_loop; + GstBus* m_bus; + GstPipeline* m_pipeline; + static float m_xrot; + static float m_yrot; + static float m_zrot; + + static void on_gst_buffer(GstElement * element, GstBuffer * buf, GstPad * pad, Pipeline* p); + static gboolean bus_call (GstBus *bus, GstMessage *msg, Pipeline* p); +}; + +#endif diff --git a/tests/examples/gl/qt/qglwtextureshare/qglrenderer.cpp b/tests/examples/gl/qt/qglwtextureshare/qglrenderer.cpp new file mode 100644 index 0000000..303e4f1 --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/qglrenderer.cpp @@ -0,0 +1,231 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * Copyright (C) 2010 Nuno Santos + * + * 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. + */ + +#include +#include +#include + +#include +#include + +#include "gstthread.h" +#include "qglrenderer.h" +#include "pipeline.h" + +#if defined(Q_WS_MAC) +extern void *qt_current_nsopengl_context(); +#endif + +QGLRenderer::QGLRenderer(const QString &videoLocation, + QWidget *parent) + : QGLWidget(parent), + videoLoc(videoLocation), + gst_thread(NULL), + closing(false), + frame(NULL) +{ + move(20, 10); + resize(640, 480); +} + +QGLRenderer::~QGLRenderer() +{ +} + +void +QGLRenderer::initializeGL() +{ + GstGLContext *context; + GstGLDisplay *display; + + display = gst_gl_display_new (); + + /* FIXME: Allow the choice at runtime */ +#if defined(GST_GL_HAVE_PLATFORM_WGL) + context = gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (), GST_GL_PLATFORM_WGL, GST_GL_API_OPENGL); +#elif defined (GST_GL_HAVE_PLATFORM_COCOA) + context = gst_gl_context_new_wrapped (display, (guintptr) qt_current_nsopengl_context(), GST_GL_PLATFORM_COCOA, GST_GL_API_OPENGL); +#elif defined(GST_GL_HAVE_PLATFORM_GLX) + context = gst_gl_context_new_wrapped (display, (guintptr) glXGetCurrentContext (), GST_GL_PLATFORM_GLX, GST_GL_API_OPENGL); +#endif + gst_object_unref (display); + + // We need to unset Qt context before initializing gst-gl plugin. + // Otherwise the attempt to share gst-gl context with Qt will fail. + this->doneCurrent(); + this->gst_thread = + new GstThread(context, this->videoLoc, SLOT(newFrame()), this); + this->makeCurrent(); + + QObject::connect(this->gst_thread, SIGNAL(finished()), + this, SLOT(close())); + QObject::connect(this, SIGNAL(closeRequested()), + this->gst_thread, SLOT(stop()), Qt::QueuedConnection); + + qglClearColor(QApplication::palette().color(QPalette::Active, + QPalette::Window)); + //glShadeModel(GL_FLAT); + //glEnable(GL_DEPTH_TEST); + //glEnable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); // Enable Texture Mapping + + this->gst_thread->start(); +} + +void +QGLRenderer::resizeGL(int width, int height) +{ + // Reset The Current Viewport And Perspective Transformation + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 100.0f); + glMatrixMode(GL_MODELVIEW); +} + +void +QGLRenderer::newFrame() +{ + Pipeline *pipeline = this->gst_thread->getPipeline(); + if(!pipeline) + return; + + /* frame is initialized as null */ + if (this->frame) + pipeline->queue_output_buf.put(this->frame); + + this->frame = pipeline->queue_input_buf.get(); + + /* direct call to paintGL (no queued) */ + this->updateGL(); +} + +void +QGLRenderer::paintGL() +{ + static GLfloat xrot = 0; + static GLfloat yrot = 0; + static GLfloat zrot = 0; + + if (this->frame) + { + guint tex_id; + GstMemory *mem; + GstVideoInfo v_info; + GstVideoFrame v_frame; + GstVideoMeta *v_meta; + + mem = gst_buffer_peek_memory (this->frame, 0); + v_meta = gst_buffer_get_video_meta (this->frame); + + if (gst_is_gl_memory (mem)) { + gst_video_info_set_format (&v_info, v_meta->format, v_meta->width, + v_meta->height); + + gst_video_frame_map (&v_frame, &v_info, this->frame, + (GstMapFlags) (GST_MAP_READ | GST_MAP_GL)); + + tex_id = *(guint *) v_frame.data[0]; + } + + glEnable(GL_DEPTH_TEST); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tex_id); + if(glGetError () != GL_NO_ERROR) + { + qDebug ("failed to bind texture that comes from gst-gl"); + emit closeRequested(); + return; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTranslatef(0.0f,0.0f,-5.0f); + + glRotatef(xrot,1.0f,0.0f,0.0f); + glRotatef(yrot,0.0f,1.0f,0.0f); + glRotatef(zrot,0.0f,0.0f,1.0f); + + glBegin(GL_QUADS); + // Front Face + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + // Back Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + // Top Face + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); + // Bottom Face + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + // Right face + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); + // Left Face + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot+=0.3f; + yrot+=0.2f; + zrot+=0.4f; + + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +void +QGLRenderer::closeEvent(QCloseEvent* event) +{ + if(this->closing == false) + { + this->closing = true; + emit closeRequested(); + event->ignore(); + } +} diff --git a/tests/examples/gl/qt/qglwtextureshare/qglrenderer.h b/tests/examples/gl/qt/qglwtextureshare/qglrenderer.h new file mode 100644 index 0000000..0bf354b --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/qglrenderer.h @@ -0,0 +1,60 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * Copyright (C) 2009 Andrey Nechypurenko + * + * 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. + */ + +#ifndef QGLRENDERER_H +#define QGLRENDERER_H + +#include + +#include + + +class GstThread; + +class QGLRenderer : public QGLWidget +{ + Q_OBJECT + +public: + QGLRenderer(const QString &videoLocation, QWidget *parent = 0); + ~QGLRenderer(); + + void closeEvent(QCloseEvent* event); + +Q_SIGNALS: + void closeRequested(); + +public Q_SLOTS: + void newFrame(); + +protected: + virtual void initializeGL(); + virtual void resizeGL(int width, int height); + virtual void paintGL(); + +private: + QString videoLoc; + GstThread *gst_thread; + bool closing; + GstBuffer *frame; +}; + +#endif // QGLRENDERER_H diff --git a/tests/examples/gl/qt/qglwtextureshare/qglwtextureshare b/tests/examples/gl/qt/qglwtextureshare/qglwtextureshare new file mode 100755 index 0000000..c32aedf Binary files /dev/null and b/tests/examples/gl/qt/qglwtextureshare/qglwtextureshare differ diff --git a/tests/examples/gl/qt/qglwtextureshare/qglwtextureshare.pro b/tests/examples/gl/qt/qglwtextureshare/qglwtextureshare.pro new file mode 100644 index 0000000..13014ac --- /dev/null +++ b/tests/examples/gl/qt/qglwtextureshare/qglwtextureshare.pro @@ -0,0 +1,83 @@ +TEMPLATE = app +TARGET = qglwtextureshare +QT += opengl + +# Add console to the CONFIG to see debug messages printed in +# the console on Windows +# CONFIG += console +DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB + +win32 { +DEFINES += WIN32 +INCLUDEPATH += \ + C:/gstreamer/include \ + C:/gstreamer/include/libxml2 \ + C:/gstreamer/include/glib-2.0 \ + C:/gstreamer/lib/glib-2.0/include \ + C:/gstreamer/include/gstreamer-1.0 +LIBS += -L"C:/gstreamer/lib" \ + -L"C:/gstreamer/bin" \ + -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lgstvideo-1.0 \ + -lopengl32 \ + -lglu32 +} +unix:!mac { + DEFINES += UNIX + INCLUDEPATH += /home/matt/Projects/jhbuild/native/usr/include/gstreamer-1.0 \ + /home/matt/Projects/jhbuild/native/usr/include/glib-2.0 \ + /home/matt/Projects/jhbuild/native/usr/lib/glib-2.0/include \ + /usr/include/gstreamer-1.0 \ + /usr/local/include/gstreamer-1.0 \ + /usr/include/glib-2.0 \ + /usr/lib/glib-2.0/include \ + /usr/include/libxml2 + LIBS += -L/home/matt/Projects/jhbuild/native/usr/lib \ + -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lgstgl-1.0 \ + -lGLU \ + -lGL +} +mac { + DEFINES += MACOSX + INCLUDEPATH += /opt/local/include/ \ + /opt/local/include/gstreamer-1.0/ \ + /opt/local/include/glib-2.0/ \ + /opt/local/lib/glib-2.0/include \ + /opt/local/include/libxml2 + LIBS += -L/opt/local/lib \ + -lgstreamer-1.0 \ + -lgstapp-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgobject-2.0 \ + -lcxcore \ + -lcvaux \ + -lcv + OBJECTIVE_SOURCES += cocoa_utils.mm + LIBS += -framework AppKit +} +DEPENDPATH += . + +# Header files +HEADERS += gstthread.h \ + pipeline.h \ + qglrenderer.h \ + AsyncQueue.h \ + glcontextid.h + +# Source files +SOURCES += gstthread.cpp \ + main.cpp \ + pipeline.cpp \ + qglrenderer.cpp diff --git a/tests/examples/gl/qt/videooverlay/Debug/videooverlay b/tests/examples/gl/qt/videooverlay/Debug/videooverlay new file mode 100755 index 0000000..3651f2b Binary files /dev/null and b/tests/examples/gl/qt/videooverlay/Debug/videooverlay differ diff --git a/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_gstthread.cpp b/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_gstthread.cpp new file mode 100644 index 0000000..7982306 --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_gstthread.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'gstthread.h' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "../../gstthread.h" +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'gstthread.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.1. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_GstThread_t { + QByteArrayData data[8]; + char stringdata[69]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_GstThread_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_GstThread_t qt_meta_stringdata_GstThread = { + { +QT_MOC_LITERAL(0, 0, 9), +QT_MOC_LITERAL(1, 10, 15), +QT_MOC_LITERAL(2, 26, 0), +QT_MOC_LITERAL(3, 27, 5), +QT_MOC_LITERAL(4, 33, 6), +QT_MOC_LITERAL(5, 40, 15), +QT_MOC_LITERAL(6, 56, 6), +QT_MOC_LITERAL(7, 63, 4) + }, + "GstThread\0resizeRequested\0\0width\0" + "height\0exposeRequested\0resize\0stop\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_GstThread[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 4, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 1, // signalCount + + // signals: name, argc, parameters, tag, flags + 1, 2, 34, 2, 0x06, + + // slots: name, argc, parameters, tag, flags + 5, 0, 39, 2, 0x0a, + 6, 2, 40, 2, 0x0a, + 7, 0, 45, 2, 0x0a, + + // signals: parameters + QMetaType::Void, QMetaType::Int, QMetaType::Int, 3, 4, + + // slots: parameters + QMetaType::Void, + QMetaType::Void, QMetaType::Int, QMetaType::Int, 3, 4, + QMetaType::Void, + + 0 // eod +}; + +void GstThread::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + GstThread *_t = static_cast(_o); + switch (_id) { + case 0: _t->resizeRequested((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + case 1: _t->exposeRequested(); break; + case 2: _t->resize((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + case 3: _t->stop(); break; + default: ; + } + } else if (_c == QMetaObject::IndexOfMethod) { + int *result = reinterpret_cast(_a[0]); + void **func = reinterpret_cast(_a[1]); + { + typedef void (GstThread::*_t)(int , int ); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&GstThread::resizeRequested)) { + *result = 0; + } + } + } +} + +const QMetaObject GstThread::staticMetaObject = { + { &QThread::staticMetaObject, qt_meta_stringdata_GstThread.data, + qt_meta_data_GstThread, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *GstThread::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *GstThread::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_GstThread.stringdata)) + return static_cast(const_cast< GstThread*>(this)); + return QThread::qt_metacast(_clname); +} + +int GstThread::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QThread::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 4) + qt_static_metacall(this, _c, _id, _a); + _id -= 4; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 4) + *reinterpret_cast(_a[0]) = -1; + _id -= 4; + } + return _id; +} + +// SIGNAL 0 +void GstThread::resizeRequested(int _t1, int _t2) +{ + void *_a[] = { 0, const_cast(reinterpret_cast(&_t1)), const_cast(reinterpret_cast(&_t2)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} +QT_END_MOC_NAMESPACE diff --git a/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_pipeline.cpp b/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_pipeline.cpp new file mode 100644 index 0000000..dff3f77 --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_pipeline.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'pipeline.h' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "../../pipeline.h" +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'pipeline.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.1. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_Pipeline_t { + QByteArrayData data[6]; + char stringdata[54]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_Pipeline_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_Pipeline_t qt_meta_stringdata_Pipeline = { + { +QT_MOC_LITERAL(0, 0, 8), +QT_MOC_LITERAL(1, 9, 15), +QT_MOC_LITERAL(2, 25, 0), +QT_MOC_LITERAL(3, 26, 5), +QT_MOC_LITERAL(4, 32, 6), +QT_MOC_LITERAL(5, 39, 13) + }, + "Pipeline\0resizeRequested\0\0width\0height\0" + "stopRequested\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_Pipeline[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 2, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 2, // signalCount + + // signals: name, argc, parameters, tag, flags + 1, 2, 24, 2, 0x06, + 5, 0, 29, 2, 0x06, + + // signals: parameters + QMetaType::Void, QMetaType::Int, QMetaType::Int, 3, 4, + QMetaType::Void, + + 0 // eod +}; + +void Pipeline::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Pipeline *_t = static_cast(_o); + switch (_id) { + case 0: _t->resizeRequested((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + case 1: _t->stopRequested(); break; + default: ; + } + } else if (_c == QMetaObject::IndexOfMethod) { + int *result = reinterpret_cast(_a[0]); + void **func = reinterpret_cast(_a[1]); + { + typedef void (Pipeline::*_t)(int , int ); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Pipeline::resizeRequested)) { + *result = 0; + } + } + { + typedef void (Pipeline::*_t)(); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Pipeline::stopRequested)) { + *result = 1; + } + } + } +} + +const QMetaObject Pipeline::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_Pipeline.data, + qt_meta_data_Pipeline, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *Pipeline::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *Pipeline::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Pipeline.stringdata)) + return static_cast(const_cast< Pipeline*>(this)); + return QObject::qt_metacast(_clname); +} + +int Pipeline::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 2) + qt_static_metacall(this, _c, _id, _a); + _id -= 2; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 2) + *reinterpret_cast(_a[0]) = -1; + _id -= 2; + } + return _id; +} + +// SIGNAL 0 +void Pipeline::resizeRequested(int _t1, int _t2) +{ + void *_a[] = { 0, const_cast(reinterpret_cast(&_t1)), const_cast(reinterpret_cast(&_t2)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} + +// SIGNAL 1 +void Pipeline::stopRequested() +{ + QMetaObject::activate(this, &staticMetaObject, 1, 0); +} +QT_END_MOC_NAMESPACE diff --git a/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_qrenderer.cpp b/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_qrenderer.cpp new file mode 100644 index 0000000..007373b --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/GeneratedFiles/debug/moc_qrenderer.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'qrenderer.h' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "../../qrenderer.h" +#include +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'qrenderer.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.2.1. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +struct qt_meta_stringdata_QRenderer_t { + QByteArrayData data[7]; + char stringdata[72]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + offsetof(qt_meta_stringdata_QRenderer_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + ) +static const qt_meta_stringdata_QRenderer_t qt_meta_stringdata_QRenderer = { + { +QT_MOC_LITERAL(0, 0, 9), +QT_MOC_LITERAL(1, 10, 15), +QT_MOC_LITERAL(2, 26, 0), +QT_MOC_LITERAL(3, 27, 14), +QT_MOC_LITERAL(4, 42, 15), +QT_MOC_LITERAL(5, 58, 5), +QT_MOC_LITERAL(6, 64, 6) + }, + "QRenderer\0exposeRequested\0\0closeRequested\0" + "resizeRequested\0width\0height\0" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_QRenderer[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 3, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 2, // signalCount + + // signals: name, argc, parameters, tag, flags + 1, 0, 29, 2, 0x06, + 3, 0, 30, 2, 0x06, + + // slots: name, argc, parameters, tag, flags + 4, 2, 31, 2, 0x0a, + + // signals: parameters + QMetaType::Void, + QMetaType::Void, + + // slots: parameters + QMetaType::Void, QMetaType::Int, QMetaType::Int, 5, 6, + + 0 // eod +}; + +void QRenderer::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + QRenderer *_t = static_cast(_o); + switch (_id) { + case 0: _t->exposeRequested(); break; + case 1: _t->closeRequested(); break; + case 2: _t->resizeRequested((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; + default: ; + } + } else if (_c == QMetaObject::IndexOfMethod) { + int *result = reinterpret_cast(_a[0]); + void **func = reinterpret_cast(_a[1]); + { + typedef void (QRenderer::*_t)(); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&QRenderer::exposeRequested)) { + *result = 0; + } + } + { + typedef void (QRenderer::*_t)(); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&QRenderer::closeRequested)) { + *result = 1; + } + } + } +} + +const QMetaObject QRenderer::staticMetaObject = { + { &QWidget::staticMetaObject, qt_meta_stringdata_QRenderer.data, + qt_meta_data_QRenderer, qt_static_metacall, 0, 0} +}; + + +const QMetaObject *QRenderer::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *QRenderer::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QRenderer.stringdata)) + return static_cast(const_cast< QRenderer*>(this)); + return QWidget::qt_metacast(_clname); +} + +int QRenderer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 3) + qt_static_metacall(this, _c, _id, _a); + _id -= 3; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 3) + *reinterpret_cast(_a[0]) = -1; + _id -= 3; + } + return _id; +} + +// SIGNAL 0 +void QRenderer::exposeRequested() +{ + QMetaObject::activate(this, &staticMetaObject, 0, 0); +} + +// SIGNAL 1 +void QRenderer::closeRequested() +{ + QMetaObject::activate(this, &staticMetaObject, 1, 0); +} +QT_END_MOC_NAMESPACE diff --git a/tests/examples/gl/qt/videooverlay/videooverlay.cpp b/tests/examples/gl/qt/videooverlay/videooverlay.cpp new file mode 100644 index 0000000..9a95e90 --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/videooverlay.cpp @@ -0,0 +1,80 @@ +/* GStreamer + * Copyright (C) <2010> Stefan Kost + * + * qt-xoverlay: demonstrate overlay handling using qt + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + gst_init (&argc, &argv); + QApplication app(argc, argv); + app.setQuitOnLastWindowClosed(true); + + /* prepare the pipeline */ + + GstElement *pipeline = gst_pipeline_new ("xvoverlay"); + GstElement *src = gst_element_factory_make ("videotestsrc", NULL); + GstElement *sink = gst_element_factory_make ("glimagesink", NULL); + + if (sink == NULL) + g_error ("Couldn't create glimagesink."); + + gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL); + gst_element_link (src, sink); + + /* prepare the ui */ + + QWidget window; + window.resize(320, 240); + window.setWindowTitle("GstVideoOverlay Qt demo"); + window.show(); + + WId xwinid = window.winId(); + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink), xwinid); + + /* run the pipeline */ + + GstStateChangeReturn sret = gst_element_set_state (pipeline, + GST_STATE_PLAYING); + if (sret == GST_STATE_CHANGE_FAILURE) { + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + /* Exit application */ + QTimer::singleShot(0, QApplication::activeWindow(), SLOT(quit())); + } + + int ret = app.exec(); + + window.hide(); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return ret; +} diff --git a/tests/examples/gl/qt/videooverlay/videooverlay.pri b/tests/examples/gl/qt/videooverlay/videooverlay.pri new file mode 100644 index 0000000..0a53a90 --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/videooverlay.pri @@ -0,0 +1,4 @@ +#Header files + +#Source files +SOURCES += videooverlay.cpp diff --git a/tests/examples/gl/qt/videooverlay/videooverlay.sln b/tests/examples/gl/qt/videooverlay/videooverlay.sln new file mode 100644 index 0000000..f566357 --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/videooverlay.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "videooverlay", "videooverlay.vcproj", "{7B8654F9-23A9-4C88-A751-6BBE09ED4CAF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7B8654F9-23A9-4C88-A751-6BBE09ED4CAF}.Debug|Win32.ActiveCfg = Debug|Win32 + {7B8654F9-23A9-4C88-A751-6BBE09ED4CAF}.Debug|Win32.Build.0 = Debug|Win32 + {7B8654F9-23A9-4C88-A751-6BBE09ED4CAF}.Release|Win32.ActiveCfg = Release|Win32 + {7B8654F9-23A9-4C88-A751-6BBE09ED4CAF}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(Qt) = preSolution + Integration = True + EndGlobalSection + GlobalSection(Qt) = preSolution + Integration = True + EndGlobalSection +EndGlobal diff --git a/tests/examples/gl/qt/videooverlay/videooverlay.vcproj b/tests/examples/gl/qt/videooverlay/videooverlay.vcproj new file mode 100644 index 0000000..557f68a --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/videooverlay.vcproj @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/qt/videooverlay/videovideooverlay.pro b/tests/examples/gl/qt/videooverlay/videovideooverlay.pro new file mode 100644 index 0000000..173b8c6 --- /dev/null +++ b/tests/examples/gl/qt/videooverlay/videovideooverlay.pro @@ -0,0 +1,53 @@ +TEMPLATE = app +TARGET = videooverlay +DESTDIR = ./Debug +CONFIG += debug +DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB +QT += gui widgets + +win32 { +DEFINES += WIN32 +INCLUDEPATH += ./GeneratedFiles \ + ./GeneratedFiles/Debug \ + C:/gstreamer/include \ + C:/gstreamer/include/libxml2 \ + C:/gstreamer/include/glib-2.0 \ + C:/gstreamer/lib/glib-2.0/include \ + C:/gstreamer/include/gstreamer-1.0 +LIBS += -L"C:/gstreamer/bin" \ + -L"C:/gstreamer/lib" \ + -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 +} + +unix { +DEFINES += UNIX +INCLUDEPATH += GeneratedFiles \ + GeneratedFiles/Debug \ + /usr/include/gstreamer-1.0 \ + /usr/local/include/gstreamer-1.0 \ + /usr/include/glib-2.0 \ + /usr/lib/glib-2.0/include \ + /usr/include/libxml2 +LIBS += -lgstreamer-1.0 \ + -lgstvideo-1.0 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lGLU \ + -lGL +} + +DEPENDPATH += . +MOC_DIR += ./GeneratedFiles/debug +OBJECTS_DIR += debug +UI_DIR += ./GeneratedFiles +RCC_DIR += ./GeneratedFiles + +#Include file(s) +include(videooverlay.pri) diff --git a/tests/examples/gl/sdl/.gitignore b/tests/examples/gl/sdl/.gitignore new file mode 100644 index 0000000..b958bef --- /dev/null +++ b/tests/examples/gl/sdl/.gitignore @@ -0,0 +1 @@ +sdlshare diff --git a/tests/examples/gl/sdl/Makefile.am b/tests/examples/gl/sdl/Makefile.am new file mode 100644 index 0000000..9e0c8c5 --- /dev/null +++ b/tests/examples/gl/sdl/Makefile.am @@ -0,0 +1,15 @@ +noinst_PROGRAMS = ## + +#works on win32 and X +if HAVE_SDL + +noinst_PROGRAMS += sdlshare + +sdlshare_SOURCES = sdlshare.c + +sdlshare_CFLAGS=$(GST_PLUGINS_GL_CFLAGS) $(GST_CFLAGS) \ + $(GL_CFLAGS) $(SDL_CFLAGS) +sdlshare_LDADD=$(GST_PLUGINS_GL_LIBS) $(GST_LIBS) \ + $(GL_LIBS) $(SDL_LIBS) + +endif diff --git a/tests/examples/gl/sdl/sdl.sln b/tests/examples/gl/sdl/sdl.sln new file mode 100644 index 0000000..0ecdd00 --- /dev/null +++ b/tests/examples/gl/sdl/sdl.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdlshare", "sdlshare.vcproj", "{2C29F5A2-5982-428A-8068-9A5788FD2277}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2C29F5A2-5982-428A-8068-9A5788FD2277}.Debug|Win32.ActiveCfg = Debug|Win32 + {2C29F5A2-5982-428A-8068-9A5788FD2277}.Debug|Win32.Build.0 = Debug|Win32 + {2C29F5A2-5982-428A-8068-9A5788FD2277}.Release|Win32.ActiveCfg = Release|Win32 + {2C29F5A2-5982-428A-8068-9A5788FD2277}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tests/examples/gl/sdl/sdlshare.c b/tests/examples/gl/sdl/sdlshare.c new file mode 100644 index 0000000..f54daf3 --- /dev/null +++ b/tests/examples/gl/sdl/sdlshare.c @@ -0,0 +1,380 @@ +/* + * GStreamer + * Copyright (C) 2009 Julien Isorce + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef WIN32 +#include +#endif + +#include +#include +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" + +#ifndef WIN32 +#include +#include "SDL/SDL_syswm.h" +#endif + +#include + + +/* hack */ +typedef struct _GstGLBuffer GstGLBuffer; +struct _GstGLBuffer +{ + GstBuffer buffer; + + GObject *obj; + + gint width; + gint height; + GLuint texture; +}; + +/* rotation angle for the triangle. */ +float rtri = 0.0f; + +/* rotation angle for the quadrilateral. */ +float rquad = 0.0f; + +/* A general OpenGL initialization function. Sets all of the initial parameters. */ +static void +InitGL (int Width, int Height) // We call this right after our OpenGL window is created. +{ + glViewport (0, 0, Width, Height); + glClearColor (0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black + glClearDepth (1.0); // Enables Clearing Of The Depth Buffer + glDepthFunc (GL_LESS); // The Type Of Depth Test To Do + glEnable (GL_DEPTH_TEST); // Enables Depth Testing + glShadeModel (GL_SMOOTH); // Enables Smooth Color Shading + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); // Reset The Projection Matrix + + gluPerspective (45.0f, (GLfloat) Width / (GLfloat) Height, 0.1f, 100.0f); // Calculate The Aspect Ratio Of The Window + + glMatrixMode (GL_MODELVIEW); +} + +/* The main drawing function. */ +static void +DrawGLScene (GstGLBuffer * gst_gl_buf) +{ + GLuint texture = gst_gl_buf->texture; + GLfloat width = (GLfloat) gst_gl_buf->width; + GLfloat height = (GLfloat) gst_gl_buf->height; + + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer + glLoadIdentity (); // Reset The View + + glTranslatef (-1.5f, 0.0f, -6.0f); // Move Left 1.5 Units And Into The Screen 6.0 + + glRotatef (rtri, 0.0f, 1.0f, 0.0f); // Rotate The Triangle On The Y axis + // draw a triangle (in smooth coloring mode) + glBegin (GL_POLYGON); // start drawing a polygon + glColor3f (1.0f, 0.0f, 0.0f); // Set The Color To Red + glVertex3f (0.0f, 1.0f, 0.0f); // Top + glColor3f (0.0f, 1.0f, 0.0f); // Set The Color To Green + glVertex3f (1.0f, -1.0f, 0.0f); // Bottom Right + glColor3f (0.0f, 0.0f, 1.0f); // Set The Color To Blue + glVertex3f (-1.0f, -1.0f, 0.0f); // Bottom Left + glEnd (); // we're done with the polygon (smooth color interpolation) + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glLoadIdentity (); // make sure we're no longer rotated. + glTranslatef (1.5f, 0.0f, -6.0f); // Move Right 3 Units, and back into the screen 6.0 + + glRotatef (rquad, 1.0f, 0.0f, 0.0f); // Rotate The Quad On The X axis + // draw a square (quadrilateral) + glColor3f (0.5f, 0.5f, 1.0f); // set color to a blue shade. + glBegin (GL_QUADS); // start drawing a polygon (4 sided) + glTexCoord3f (0.0f, height, 0.0f); + glVertex3f (-1.0f, 1.0f, 0.0f); // Top Left + glTexCoord3f (width, height, 0.0f); + glVertex3f (1.0f, 1.0f, 0.0f); // Top Right + glTexCoord3f (width, 0.0f, 0.0f); + glVertex3f (1.0f, -1.0f, 0.0f); // Bottom Right + glTexCoord3f (0.0f, 0.0f, 0.0f); + glVertex3f (-1.0f, -1.0f, 0.0f); // Bottom Left + glEnd (); // done with the polygon + + glBindTexture (GL_TEXTURE_2D, 0); + + rtri += 1.0f; // Increase The Rotation Variable For The Triangle + rquad -= 1.0f; // Decrease The Rotation Variable For The Quad + + // swap buffers to display, since we're double buffered. + SDL_GL_SwapBuffers (); +} + +static gboolean +update_sdl_scene (void *fk) +{ + GstElement *fakesink = (GstElement *) fk; + GMainLoop *loop = + (GMainLoop *) g_object_get_data (G_OBJECT (fakesink), "loop"); + GAsyncQueue *queue_input_buf = + (GAsyncQueue *) g_object_get_data (G_OBJECT (fakesink), + "queue_input_buf"); + GAsyncQueue *queue_output_buf = + (GAsyncQueue *) g_object_get_data (G_OBJECT (fakesink), + "queue_output_buf"); + GstGLBuffer *gst_gl_buf = (GstGLBuffer *) g_async_queue_pop (queue_input_buf); + + SDL_Event event; + while (SDL_PollEvent (&event)) { + if (event.type == SDL_QUIT) { + g_main_loop_quit (loop); + } + if (event.type == SDL_KEYDOWN) { + if (event.key.keysym.sym == SDLK_ESCAPE) { + g_main_loop_quit (loop); + } + } + } + + DrawGLScene (gst_gl_buf); + + /* push buffer so it can be unref later */ + g_async_queue_push (queue_output_buf, gst_gl_buf); + + return FALSE; +} + +/* fakesink handoff callback */ +static void +on_gst_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad, + gpointer data) +{ + GAsyncQueue *queue_input_buf = NULL; + GAsyncQueue *queue_output_buf = NULL; + + /* ref then push buffer to use it in sdl */ + gst_buffer_ref (buf); + queue_input_buf = + (GAsyncQueue *) g_object_get_data (G_OBJECT (fakesink), + "queue_input_buf"); + g_async_queue_push (queue_input_buf, buf); + if (g_async_queue_length (queue_input_buf) > 3) + g_idle_add (update_sdl_scene, (gpointer) fakesink); + + /* pop then unref buffer we have finished to use in sdl */ + queue_output_buf = + (GAsyncQueue *) g_object_get_data (G_OBJECT (fakesink), + "queue_output_buf"); + if (g_async_queue_length (queue_output_buf) > 3) { + GstBuffer *buf_old = (GstBuffer *) g_async_queue_pop (queue_output_buf); + gst_buffer_unref (buf_old); + } +} + +/* gst bus signal watch callback */ +static void +end_stream_cb (GstBus * bus, GstMessage * msg, GMainLoop * loop) +{ + switch (GST_MESSAGE_TYPE (msg)) { + + case GST_MESSAGE_EOS: + g_print ("End-of-stream\n"); + g_print + ("For more information, try to run: GST_DEBUG=gldisplay:2 ./sdlshare\n"); + break; + + case GST_MESSAGE_ERROR: + { + 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 deails: %s\n", debug); + g_free (debug); + } + + break; + } + + default: + break; + } + + g_main_loop_quit (loop); +} + +int +main (int argc, char **argv) +{ + +#ifdef WIN32 + HGLRC sdl_gl_context = 0; + HDC sdl_dc = 0; +#else + SDL_SysWMinfo info; + Display *sdl_display = NULL; + Window sdl_win = 0; + GLXContext sdl_gl_context = NULL; +#endif + + GMainLoop *loop = NULL; + GstPipeline *pipeline = NULL; + GstBus *bus = NULL; + GstElement *glfilter = NULL; + GstElement *fakesink = NULL; + GstState state; + GAsyncQueue *queue_input_buf = NULL; + GAsyncQueue *queue_output_buf = NULL; + + /* Initialize SDL for video output */ + if (SDL_Init (SDL_INIT_VIDEO) < 0) { + fprintf (stderr, "Unable to initialize SDL: %s\n", SDL_GetError ()); + return -1; + } + + /* Create a 640x480 OpenGL screen */ + if (SDL_SetVideoMode (640, 480, 0, SDL_OPENGL) == NULL) { + fprintf (stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError ()); + SDL_Quit (); + return -1; + } + + /* Set the title bar in environments that support it */ + SDL_WM_SetCaption ("SDL and gst-plugins-gl", NULL); + + + /* Loop, drawing and checking events */ + InitGL (640, 480); + + gst_init (&argc, &argv); + loop = g_main_loop_new (NULL, FALSE); + + /* retrieve and turn off sdl opengl context */ +#ifdef WIN32 + sdl_gl_context = wglGetCurrentContext (); + sdl_dc = wglGetCurrentDC (); + wglMakeCurrent (0, 0); +#else + SDL_VERSION (&info.version); + SDL_GetWMInfo (&info); + sdl_display = info.info.x11.display; + sdl_win = info.info.x11.window; + sdl_gl_context = glXGetCurrentContext (); + glXMakeCurrent (sdl_display, None, 0); +#endif + + pipeline = + GST_PIPELINE (gst_parse_launch + ("videotestsrc ! video/x-raw, width=320, height=240, framerate=(fraction)30/1 ! " + "gleffects effect=5 ! fakesink sync=1", NULL)); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message::error", G_CALLBACK (end_stream_cb), loop); + g_signal_connect (bus, "message::warning", G_CALLBACK (end_stream_cb), loop); + g_signal_connect (bus, "message::eos", G_CALLBACK (end_stream_cb), loop); + gst_object_unref (bus); + + /* sdl_gl_context is an external OpenGL context with which gst-plugins-gl want to share textures */ + glfilter = gst_bin_get_by_name (GST_BIN (pipeline), "gleffects0"); + g_object_set (G_OBJECT (glfilter), "external-opengl-context", + sdl_gl_context, NULL); + gst_object_unref (glfilter); + + /* NULL to PAUSED state pipeline to make sure the gst opengl context is created and + * shared with the sdl one */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED); + state = GST_STATE_PAUSED; + if (gst_element_get_state (GST_ELEMENT (pipeline), &state, NULL, + GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS) { + g_debug ("failed to pause pipeline\n"); + return -1; + } + + /* turn on back sdl opengl context */ +#ifdef WIN32 + wglMakeCurrent (sdl_dc, sdl_gl_context); +#else + glXMakeCurrent (sdl_display, sdl_win, sdl_gl_context); +#endif + + /* append a gst-gl texture to this queue when you do not need it no more */ + fakesink = gst_bin_get_by_name (GST_BIN (pipeline), "fakesink0"); + g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL); + g_signal_connect (fakesink, "handoff", G_CALLBACK (on_gst_buffer), NULL); + queue_input_buf = g_async_queue_new (); + queue_output_buf = g_async_queue_new (); + g_object_set_data (G_OBJECT (fakesink), "queue_input_buf", queue_input_buf); + g_object_set_data (G_OBJECT (fakesink), "queue_output_buf", queue_output_buf); + g_object_set_data (G_OBJECT (fakesink), "loop", loop); + gst_object_unref (fakesink); + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + + g_main_loop_run (loop); + + /* before to deinitialize the gst-gl-opengl context, + * no shared context (here the sdl one) must be current + */ +#ifdef WIN32 + wglMakeCurrent (0, 0); +#else + glXMakeCurrent (sdl_display, sdl_win, sdl_gl_context); +#endif + + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + gst_object_unref (pipeline); + + /* turn on back sdl opengl context */ +#ifdef WIN32 + wglMakeCurrent (sdl_dc, sdl_gl_context); +#else + glXMakeCurrent (sdl_display, None, 0); +#endif + + SDL_Quit (); + + /* make sure there is no pending gst gl buffer in the communication queues + * between sdl and gst-gl + */ + while (g_async_queue_length (queue_input_buf) > 0) { + GstBuffer *buf = (GstBuffer *) g_async_queue_pop (queue_input_buf); + gst_buffer_unref (buf); + } + + while (g_async_queue_length (queue_output_buf) > 0) { + GstBuffer *buf = (GstBuffer *) g_async_queue_pop (queue_output_buf); + gst_buffer_unref (buf); + } + + return 0; +} diff --git a/tests/examples/gl/sdl/sdlshare.vcproj b/tests/examples/gl/sdl/sdlshare.vcproj new file mode 100644 index 0000000..760b349 --- /dev/null +++ b/tests/examples/gl/sdl/sdlshare.vcproj @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +