-
-
Save lubosz/d34d73f06c50709e43c1 to your computer and use it in GitHub Desktop.
minimal code for reproduction of T399
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| cmake_minimum_required(VERSION 2.8) | |
| find_package(PkgConfig) | |
| pkg_check_modules(GST REQUIRED gstreamer-1.0>=1.4.5 | |
| gstreamer-base-1.0>=1.4.5 | |
| gstreamer-gl-1.0>=1.4.5 | |
| gstreamer-video-1.0>=1.4.5 | |
| gstreamer-app-1.0>=1.4.5) | |
| pkg_check_modules(GTK REQUIRED gtk+-3.0>=3.16.2) | |
| include_directories( | |
| ${GST_INCLUDE_DIRS} | |
| ${GTK_INCLUDE_DIRS} | |
| ) | |
| link_directories ( | |
| ${GST_LIBRARY_DIRS} | |
| ${GTK_LIBRARY_DIRS} | |
| ) | |
| add_executable(bug main.c) | |
| target_link_libraries ( | |
| bug | |
| ${GST_LIBRARIES} | |
| ${GTK_LIBRARIES} | |
| gstreamer-1.0 gstvideo-1.0 gstbase-1.0 | |
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdlib.h> | |
| #include <gtk/gtk.h> | |
| #include <gdk/gdkx.h> | |
| #include <gst/gst.h> | |
| #include <gst/app/app.h> | |
| #include <gst/video/videooverlay.h> | |
| #include <glib-object.h> | |
| G_BEGIN_DECLS | |
| #define BUG_TYPE_CLIENT \ | |
| (bug_client_get_type ()) | |
| #define BUG_CLIENT(obj) \ | |
| (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUG_TYPE_CLIENT, \ | |
| BugClient)) | |
| #define BUG_IS_CLIENT(obj) \ | |
| (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUG_TYPE_CLIENT)) | |
| typedef struct _BugClient BugClient; | |
| typedef struct _BugClientClass BugClientClass; | |
| G_END_DECLS GST_DEBUG_CATEGORY_STATIC (debug_category); | |
| #define GST_CAT_DEFAULT debug_category | |
| typedef enum | |
| { | |
| BUG_CLIENT_STATE_STOPPED, | |
| BUG_CLIENT_STATE_PLAYING, | |
| BUG_CLIENT_STATE_PAUSED, | |
| } BugClientState; | |
| struct _BugClient | |
| { | |
| GObject parent_instance; | |
| BugClientState state; | |
| GstElement *pipeline; | |
| GstElement *stream_bin; | |
| GstElement *stream_decodebin; | |
| GstPad *stream_src_pad; | |
| gulong stream_pad_probe_id; | |
| GstElement *still_bin; | |
| GstElement *still_decodebin; | |
| GstPad *still_src_pad; | |
| GstElement *video_bin; | |
| GstElement *glimagesink; | |
| GstPad *video_sink_pad; | |
| gchar *stream_uri; | |
| gchar *frame_uri; | |
| }; | |
| struct _BugClientClass | |
| { | |
| GObjectClass parent_class; | |
| }; | |
| G_DEFINE_TYPE (BugClient, bug_client, G_TYPE_OBJECT) | |
| static void update_frame_image (BugClient * self) | |
| { | |
| gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_start ()); | |
| gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_stop (FALSE)); | |
| if (self->frame_uri != NULL) { | |
| gst_element_set_state (self->still_bin, GST_STATE_READY); | |
| g_object_set ((GObject *) self->still_decodebin, | |
| "uri", self->frame_uri, NULL); | |
| gst_element_set_state (self->still_bin, GST_STATE_PAUSED); | |
| } | |
| } | |
| static GstPadProbeReturn | |
| stream_pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) | |
| { | |
| return GST_PAD_PROBE_OK; | |
| } | |
| static void | |
| stream_pad_probe_remove (BugClient * self) | |
| { | |
| if (self->stream_pad_probe_id != 0) { | |
| gst_pad_remove_probe (self->stream_src_pad, self->stream_pad_probe_id); | |
| self->stream_pad_probe_id = 0; | |
| } | |
| } | |
| void | |
| bug_client_stop (BugClient * self) | |
| { | |
| GstState state; | |
| if (self->state == BUG_CLIENT_STATE_STOPPED) | |
| return; | |
| GST_DEBUG ("Transition to Stop"); | |
| //gst_element_set_state (self->still_bin, GST_STATE_NULL); // freezes | |
| gst_element_set_state (self->pipeline, GST_STATE_NULL); | |
| gst_element_get_state (self->pipeline, &state, NULL, GST_CLOCK_TIME_NONE); | |
| g_assert (state == GST_STATE_NULL); | |
| if (self->state == BUG_CLIENT_STATE_PAUSED) { | |
| GST_DEBUG ("Unlink still_src_pad"); | |
| gst_pad_unlink (self->still_src_pad, self->video_sink_pad); | |
| } else { | |
| GST_DEBUG ("Unlink stream_src_pad"); | |
| gst_pad_unlink (self->stream_src_pad, self->video_sink_pad); | |
| } | |
| stream_pad_probe_remove (self); | |
| self->state = BUG_CLIENT_STATE_STOPPED; | |
| } | |
| void | |
| bug_client_pause (BugClient * self) | |
| { | |
| GstState state; | |
| if (self->state == BUG_CLIENT_STATE_PAUSED) | |
| return; | |
| GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (self->pipeline), | |
| GST_DEBUG_GRAPH_SHOW_ALL, | |
| "onpause"); | |
| gst_element_set_state (self->pipeline, GST_STATE_PAUSED); | |
| gst_element_get_state (self->pipeline, &state, NULL, GST_CLOCK_TIME_NONE); | |
| g_assert (state == GST_STATE_PAUSED); | |
| if (self->state == BUG_CLIENT_STATE_PLAYING) { | |
| GST_DEBUG ("Transition from PLAYING to PAUSED"); | |
| GST_DEBUG ("Unlink stream_src_pad"); | |
| gst_pad_unlink (self->stream_src_pad, self->video_sink_pad); | |
| } | |
| GST_DEBUG ("Link still_src_pad"); | |
| gst_pad_link (self->still_src_pad, self->video_sink_pad); | |
| update_frame_image (self); | |
| GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (self->pipeline), | |
| GST_DEBUG_GRAPH_SHOW_ALL, | |
| "relink"); | |
| self->state = BUG_CLIENT_STATE_PAUSED; | |
| } | |
| void | |
| bug_client_play (BugClient * self) | |
| { | |
| if (self->state == BUG_CLIENT_STATE_PLAYING) | |
| return; | |
| if (self->state == BUG_CLIENT_STATE_PAUSED) { | |
| GST_DEBUG ("Transition from PAUSED to PLAYING"); | |
| gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_start ()); | |
| gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_stop (FALSE)); | |
| GST_DEBUG ("Unlink still_src_pad"); | |
| gst_element_set_state (self->still_bin, GST_STATE_READY); | |
| gst_pad_unlink (self->still_src_pad, self->video_sink_pad); | |
| g_clear_pointer (&self->frame_uri, g_free); | |
| } | |
| GST_DEBUG ("Link stream_src_pad"); | |
| gst_pad_link (self->stream_src_pad, self->video_sink_pad); | |
| gst_element_set_state (self->pipeline, GST_STATE_PLAYING); | |
| stream_pad_probe_remove (self); | |
| self->state = BUG_CLIENT_STATE_PLAYING; | |
| } | |
| static gboolean | |
| is_video_pad (GstPad * pad) | |
| { | |
| GstCaps *caps; | |
| GstStructure *structure; | |
| const gchar *type; | |
| caps = gst_pad_query_caps (pad, NULL); | |
| structure = gst_caps_get_structure (caps, 0); | |
| type = gst_structure_get_name (structure); | |
| if (g_str_has_prefix (type, "video/")) | |
| return TRUE; | |
| return FALSE; | |
| } | |
| static void | |
| stream_decodebin_pad_added_cb (GstElement * element, | |
| GstPad * src_pad, BugClient * self) | |
| { | |
| if (is_video_pad (src_pad)) | |
| gst_ghost_pad_set_target (GST_GHOST_PAD (self->stream_src_pad), src_pad); | |
| } | |
| static void | |
| still_decodebin_pad_added_cb (GstElement * element, | |
| GstPad * src_pad, BugClient * self) | |
| { | |
| if (is_video_pad (src_pad)) | |
| gst_ghost_pad_set_target (GST_GHOST_PAD (self->still_src_pad), src_pad); | |
| } | |
| static void | |
| create_stream_bin (BugClient * self) | |
| { | |
| self->stream_bin = gst_bin_new (NULL); | |
| self->stream_decodebin = gst_element_factory_make ("uridecodebin", NULL); | |
| g_assert (self->stream_decodebin != NULL); | |
| gst_bin_add_many (GST_BIN (self->stream_bin), self->stream_decodebin, NULL); | |
| /* Add a ghost pad */ | |
| self->stream_src_pad = gst_ghost_pad_new_no_target (NULL, GST_PAD_SRC); | |
| gst_element_add_pad (self->stream_bin, self->stream_src_pad); | |
| g_signal_connect (self->stream_decodebin, "pad-added", | |
| G_CALLBACK (stream_decodebin_pad_added_cb), self); | |
| } | |
| static void | |
| create_still_bin (BugClient * self) | |
| { | |
| self->still_bin = gst_bin_new (NULL); | |
| self->still_decodebin = gst_element_factory_make ("uridecodebin", NULL); | |
| g_assert (self->still_decodebin != NULL); | |
| gst_bin_add_many (GST_BIN (self->still_bin), self->still_decodebin, NULL); | |
| /* Add a ghost pad */ | |
| self->still_src_pad = gst_ghost_pad_new_no_target (NULL, GST_PAD_SRC); | |
| gst_element_add_pad (self->still_bin, self->still_src_pad); | |
| g_signal_connect (self->still_decodebin, "pad-added", | |
| G_CALLBACK (still_decodebin_pad_added_cb), self); | |
| } | |
| static void | |
| create_video_bin (BugClient * self) | |
| { | |
| GstElement *glupload; | |
| GstPad *sink_pad; | |
| self->video_bin = gst_bin_new (NULL); | |
| glupload = gst_element_factory_make ("glupload", NULL); | |
| self->glimagesink = gst_element_factory_make ("glimagesink", NULL); | |
| g_assert (glupload != NULL); | |
| g_assert (self->glimagesink != NULL); | |
| gst_bin_add_many (GST_BIN (self->video_bin), | |
| glupload, self->glimagesink, NULL); | |
| gst_element_link_many (glupload, self->glimagesink, NULL); | |
| /* Add a ghost pad */ | |
| sink_pad = gst_element_get_static_pad (glupload, "sink"); | |
| self->video_sink_pad = gst_ghost_pad_new (NULL, sink_pad); | |
| gst_element_add_pad (self->video_bin, self->video_sink_pad); | |
| gst_object_unref (sink_pad); | |
| } | |
| static void | |
| bug_client_init (BugClient * self) | |
| {} | |
| static void | |
| bug_client_constructed (GObject * object) | |
| { | |
| BugClient *self = (BugClient *) object; | |
| G_OBJECT_CLASS (bug_client_parent_class)->constructed (object); | |
| /* Create pipeline */ | |
| self->pipeline = gst_pipeline_new (NULL); | |
| create_stream_bin (self); | |
| create_still_bin (self); | |
| create_video_bin (self); | |
| gst_bin_add_many (GST_BIN (self->pipeline), | |
| self->stream_bin, self->still_bin, self->video_bin, NULL); | |
| /* We are ready */ | |
| gst_element_set_state (self->pipeline, GST_STATE_READY); | |
| gst_element_set_locked_state (self->still_bin, TRUE); | |
| } | |
| static void | |
| bug_client_finalize (GObject * object) | |
| { | |
| BugClient *self = (BugClient *) object; | |
| gst_element_set_state (self->still_bin, GST_STATE_NULL); | |
| gst_element_set_state (self->pipeline, GST_STATE_NULL); | |
| gst_object_unref (self->pipeline); | |
| g_free (self->stream_uri); | |
| g_free (self->frame_uri); | |
| G_OBJECT_CLASS (bug_client_parent_class)->finalize (object); | |
| } | |
| static void | |
| bug_client_class_init (BugClientClass * klass) | |
| { | |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
| GST_DEBUG_CATEGORY_INIT (debug_category, "bugclient", 0, "Bug Client"); | |
| gst_debug_set_threshold_for_name ("glcontext", 5); | |
| //gst_debug_set_threshold_for_name("glimagesink", 4); | |
| object_class->constructed = bug_client_constructed; | |
| object_class->finalize = bug_client_finalize; | |
| } | |
| BugClient * | |
| bug_client_new (void) | |
| { | |
| return g_object_new (BUG_TYPE_CLIENT, NULL); | |
| } | |
| void | |
| bug_client_set_window_handle (BugClient * self, guintptr handle) | |
| { | |
| gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (self->glimagesink), | |
| handle); | |
| } | |
| void | |
| bug_client_expose (BugClient * self) | |
| { | |
| gst_video_overlay_expose (GST_VIDEO_OVERLAY (self->glimagesink)); | |
| } | |
| void | |
| bug_client_set_uri (BugClient * self, const gchar * uri) | |
| { | |
| g_free (self->stream_uri); | |
| self->stream_uri = g_strdup (uri); | |
| if (self->state == BUG_CLIENT_STATE_STOPPED) { | |
| GST_DEBUG ("Change stream URI to %s", self->stream_uri); | |
| g_object_set (self->stream_decodebin, "uri", self->stream_uri, NULL); | |
| } | |
| } | |
| void | |
| bug_client_set_frame_uri (BugClient * self, const gchar * uri) | |
| { | |
| g_free (self->frame_uri); | |
| self->frame_uri = g_strdup (uri); | |
| if (self->state == BUG_CLIENT_STATE_PAUSED) | |
| update_frame_image (self); | |
| } | |
| typedef struct | |
| { | |
| GtkApplication *app; | |
| GtkWidget *window; | |
| GtkWidget *drawing_area; | |
| BugClient *client; | |
| gint task_number; | |
| gchar *uri; | |
| } Example; | |
| static void | |
| task_play (Example * self) | |
| { | |
| bug_client_set_uri (self->client, self->uri); | |
| bug_client_play (self->client); | |
| } | |
| static void | |
| task_resume (Example * self) | |
| { | |
| bug_client_play (self->client); | |
| } | |
| static void | |
| task_still_image_1 (Example * self) | |
| { | |
| gchar *uri = | |
| g_strdup_printf | |
| ("http://people.collabora.com/~xclaesse/sintel/still-image1.jpg"); | |
| bug_client_pause (self->client); | |
| bug_client_set_frame_uri (self->client, uri); | |
| g_free (uri); | |
| } | |
| static void | |
| task_stop (Example * self) | |
| { | |
| g_usleep (500000); | |
| bug_client_stop (self->client); | |
| } | |
| static void | |
| task_stop_quit (Example * self) | |
| { | |
| task_stop (self); | |
| g_application_quit (G_APPLICATION (self->app)); | |
| } | |
| typedef void (*TaskFunc) (Example * self); | |
| static TaskFunc tasks[] = { | |
| task_play, | |
| task_still_image_1, | |
| task_stop, | |
| task_resume, | |
| task_stop_quit | |
| }; | |
| static gboolean | |
| test_continue (gpointer user_data) | |
| { | |
| Example *self = user_data; | |
| if (self->task_number >= G_N_ELEMENTS (tasks)) { | |
| g_application_quit (G_APPLICATION (self->app)); | |
| return G_SOURCE_REMOVE; | |
| } | |
| tasks[self->task_number] (self); | |
| self->task_number++; | |
| return G_SOURCE_CONTINUE; | |
| } | |
| static void | |
| drawing_area_size_allocate_cb (GtkWidget * widget, | |
| GdkRectangle * allocation, Example * self) | |
| { | |
| if (self->client != NULL) | |
| bug_client_expose (self->client); | |
| } | |
| static void | |
| startup_cb (GApplication * app, Example * self) | |
| { | |
| self->drawing_area = gtk_drawing_area_new (); | |
| gtk_widget_set_size_request (self->drawing_area, 640, 480); | |
| g_signal_connect (self->drawing_area, "size-allocate", | |
| G_CALLBACK (drawing_area_size_allocate_cb), self); | |
| gtk_widget_show (self->drawing_area); | |
| self->window = gtk_application_window_new (self->app); | |
| gtk_container_add (GTK_CONTAINER (self->window), self->drawing_area); | |
| gtk_widget_show (self->window); | |
| gtk_widget_realize (self->drawing_area); | |
| self->client = bug_client_new (); | |
| bug_client_set_window_handle (self->client, | |
| GDK_WINDOW_XID (gtk_widget_get_window (self->drawing_area))); | |
| } | |
| static void | |
| activate_cb (GApplication * app, Example * self) | |
| { | |
| gtk_window_present (GTK_WINDOW (self->window)); | |
| } | |
| static void | |
| open_cb (GApplication * application, | |
| GFile ** files, gint n_files, gchar * hint, Example * self) | |
| { | |
| g_assert (n_files == 1); | |
| self->uri = g_file_get_uri (files[0]); | |
| if (test_continue (self)) | |
| g_timeout_add_seconds (5, test_continue, self); | |
| } | |
| gint | |
| main (gint argc, gchar * argv[]) | |
| { | |
| Example self = { NULL, }; | |
| gst_init (&argc, &argv); | |
| self.app = gtk_application_new ("org.example.BugClient", | |
| G_APPLICATION_HANDLES_OPEN); | |
| g_signal_connect (self.app, "startup", G_CALLBACK (startup_cb), &self); | |
| g_signal_connect (self.app, "activate", G_CALLBACK (activate_cb), &self); | |
| g_signal_connect (self.app, "open", G_CALLBACK (open_cb), &self); | |
| g_application_run ((GApplication *) self.app, argc, argv); | |
| g_clear_object (&self.app); | |
| g_clear_object (&self.client); | |
| g_free (self.uri); | |
| return EXIT_SUCCESS; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment