2 initially based on an example by Tristan Matthews
3 http://tristanswork.blogspot.com/2008/09/fullscreen-video-in-gstreamer-with-gtk.html
7 #include <gst/interfaces/xoverlay.h>
14 make_multi_pipeline(windows_t
*windows
, int count
)
16 GstPipeline
*pipeline
= GST_PIPELINE(gst_pipeline_new("sparrow_pipeline"));
17 GstElement
*tee
= pre_tee_pipeline(pipeline
);
21 for (i
= 0; i
< count
; i
++){
22 GstElement
*sink
= windows
->sinks
[i
];
24 //(pipeline, tee, sink, int rngseed, int colour, timer flag, int debug flag)
25 /* timer should only run on one of them. colour >= 3 is undefined */
26 int debug
= option_debug
== i
;
27 int timer
= option_timer
== i
;
28 if (option_reload
!= NULL
){
29 if (option_reload
[i
] == NULL
){
30 g_critical("You can't reload some screens and not others!");
33 reload
= option_reload
[i
];
35 if (option_save
&& option_save
[i
]){
36 save
= option_save
[i
];
38 post_tee_pipeline(pipeline
, tee
, sink
, i
, i
+ 1, timer
, debug
, save
, reload
);
41 /*add a branch saving the video to a file */
42 mjpeg_branch(pipeline
, tee
);
50 bus_call(GstBus
* bus
, GstMessage
*msg
, gpointer data
)
52 windows_t
*windows
= (windows_t
*)data
;
53 if (GST_MESSAGE_TYPE(msg
) == GST_MESSAGE_ELEMENT
&&
54 gst_structure_has_name(msg
->structure
, "prepare-xwindow-id")){
55 g_print("Got prepare-xwindow-id msg. option screens: %d\n", option_screens
);
56 for (int i
= 0; i
< option_screens
; i
++){
57 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(windows
->sinks
[i
]),
58 windows
->xwindows
[i
]);
59 g_print("connected sink %d to window %lu\n", i
, windows
->xwindows
[i
]);
60 hide_mouse(windows
->gtk_windows
[i
]);
66 toggle_fullscreen(GtkWidget
*widget
){
67 GdkWindowState state
= gdk_window_get_state(GDK_WINDOW(widget
->window
));
68 if (state
== GDK_WINDOW_STATE_FULLSCREEN
){
69 gtk_window_unfullscreen(GTK_WINDOW(widget
));
72 gtk_window_fullscreen(GTK_WINDOW(widget
));
77 key_press_event_cb(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
79 g_print("got key %c\n", event
->keyval
);
80 switch (event
->keyval
){
82 toggle_fullscreen(widget
);
85 g_signal_emit_by_name(widget
, "destroy");
93 void destroy_cb(GtkWidget
* widget
, gpointer data
)
95 GMainLoop
*loop
= (GMainLoop
*) data
;
96 g_print("Window destroyed\n");
97 g_main_loop_quit(loop
);
101 video_widget_realize_cb(GtkWidget
*widget
, gpointer data
)
103 windows_t
*windows
= (windows_t
*)data
;
104 int r
= windows
->realised
;
105 if (r
< MAX_SCREENS
){
106 windows
->xwindows
[r
] = GDK_WINDOW_XID(GDK_WINDOW(widget
->window
));
107 g_print("realised window %d with XID %lu\n", r
, windows
->xwindows
[r
]);
110 g_print("wtf, there seem to be %d windows!\n", r
);
118 set_up_window(GMainLoop
*loop
, GtkWidget
*window
, int screen_no
){
119 static const GdkColor black
= {0, 0, 0, 0};
120 gtk_window_set_default_size(GTK_WINDOW(window
), WIDTH
, HEIGHT
);
122 if (option_fullscreen
){
123 gtk_window_fullscreen(GTK_WINDOW(window
));
126 /*if more than one screen is requested, set the screen number.
127 otherwise let it fall were it falls */
128 if (option_screens
> 1 || option_first_screen
){
129 GdkScreen
* screen
= gdk_screen_get_default();
130 int width
= gdk_screen_get_width(screen
);
131 /* XXX placment heuristic is crap */
132 gtk_window_move(GTK_WINDOW(window
),
133 (width
/ 2 * screen_no
+ 200) % width
, 50);
136 // attach key press signal to key press callback
137 gtk_widget_set_events(window
, GDK_KEY_PRESS_MASK
);
138 g_signal_connect(G_OBJECT(window
), "key-press-event", G_CALLBACK(key_press_event_cb
), NULL
);
139 g_signal_connect(G_OBJECT(window
), "destroy", G_CALLBACK(destroy_cb
), loop
);
141 gtk_widget_modify_bg(window
, GTK_STATE_NORMAL
, &black
);
142 gtk_widget_show_all(window
);
147 gint
main (gint argc
, gchar
*argv
[])
149 //initialise threads before any gtk stuff (because not using gtk_init)
151 /*this is more complicated than plain gtk_init/gst_init, so that options from
152 all over can be gathered and presented together.
154 GOptionGroup
*gst_opts
= gst_init_get_option_group();
155 GOptionGroup
*gtk_opts
= gtk_get_option_group(TRUE
);
156 GOptionContext
*ctx
= g_option_context_new("...!");
157 g_option_context_add_main_entries(ctx
, entries
, NULL
);
158 g_option_context_add_group(ctx
, gst_opts
);
159 g_option_context_add_group(ctx
, gtk_opts
);
160 GError
*error
= NULL
;
161 if (!g_option_context_parse(ctx
, &argc
, &argv
, &error
)){
162 g_print ("Error initializing: %s\n", GST_STR_NULL(error
->message
));
165 g_option_context_free(ctx
);
167 GMainLoop
*loop
= g_main_loop_new(NULL
, FALSE
);
170 windows
.realised
= 0;
173 for (i
= 0; i
< option_screens
; i
++){
174 GtkWidget
*window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
175 g_signal_connect(window
, "realize",
176 G_CALLBACK(video_widget_realize_cb
), &windows
);
177 /* set up sink here */
178 GstElement
*sink
= gst_element_factory_make("ximagesink", NULL
);
179 set_up_window(loop
, window
, i
+ option_first_screen
);
180 windows
.gtk_windows
[i
] = window
;
181 windows
.sinks
[i
] = sink
;
184 GstElement
*pipeline
= (GstElement
*)make_multi_pipeline(&windows
, option_screens
);
186 GstBus
*bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
187 gst_bus_add_watch(bus
, (GstBusFunc
)bus_call
, &windows
);
188 gst_object_unref(bus
);
190 gst_element_set_state(pipeline
, GST_STATE_PLAYING
);
192 g_main_loop_run(loop
);
194 gst_element_set_state (pipeline
, GST_STATE_NULL
);
195 gst_object_unref (pipeline
);