1 /*N way video splitter */
4 #include <gst/interfaces/xoverlay.h>
12 post_tee_pipeline(GstPipeline
*pipeline
, GstElement
*tee
, GstElement
*sink
,
13 int crop_left
, int crop_right
){
14 GstElement
*queue
= gst_element_factory_make("queue", NULL
);
15 GstElement
*crop
= gst_element_factory_make("videocrop", NULL
);
17 g_object_set(G_OBJECT(crop
),
24 gst_bin_add_many(GST_BIN(pipeline
),
30 gst_element_link_many(tee
,
38 pre_tee_pipeline(GstPipeline
*pipeline
, int width
, int height
){
39 if (pipeline
== NULL
){
40 pipeline
= GST_PIPELINE(gst_pipeline_new("wha_pipeline"));
42 char * src_name
= (option_fake
) ? "videotestsrc" : "v4l2src";
43 GstElement
*src
= gst_element_factory_make(src_name
, NULL
);
44 if (option_fake
== 2){//set some properties for an interesting picture
45 g_object_set(G_OBJECT(src
),
55 GstElement
*tee
= gst_element_factory_make ("tee", NULL
);
57 caps
= gst_caps_new_simple("video/x-raw-yuv",
58 "width", G_TYPE_INT
, width
,
59 "height", G_TYPE_INT
, height
,
61 gst_caps_merge(caps
, gst_caps_new_simple("video/x-raw-rgb",
62 "width", G_TYPE_INT
, width
,
63 "height", G_TYPE_INT
, height
,
66 gst_bin_add_many(GST_BIN(pipeline
),
71 gst_element_link_filtered(src
,
78 static void hide_mouse(GtkWidget
*widget
){
79 GdkWindow
*w
= GDK_WINDOW(widget
->window
);
80 GdkDisplay
*display
= gdk_display_get_default();
81 GdkCursor
*cursor
= gdk_cursor_new_for_display(display
, GDK_BLANK_CURSOR
);
82 gdk_window_set_cursor(w
, cursor
);
83 gdk_cursor_unref (cursor
);
88 static GstBusSyncReply
89 sync_bus_call(GstBus
*bus
, GstMessage
*msg
, gpointer data
)
91 // ignore anything but 'prepare-xwindow-id' element messages
92 if ((GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_ELEMENT
) ||
93 (! gst_structure_has_name(msg
->structure
, "prepare-xwindow-id"))){
96 window_t
*windows
= (window_t
*)data
;
97 g_print("Got prepare-xwindow-id msg. \n");
98 //connect this one up with the right window.
99 GstElement
*sink
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
102 g_print("found sink %p\n", sink
);
103 for (int i
= 0; i
< option_screens
; i
++){
104 const window_t
*w
= windows
+ i
;
105 if (w
->sink
== sink
){
106 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink
), w
->xid
);
107 g_print("connected sink %d to window %lu\n", i
, w
->xid
);
108 hide_mouse(w
->widget
);
115 g_print("couldn't find a window for this sink!\n");
118 gst_message_unref(msg
);
123 toggle_fullscreen(GtkWidget
*widget
){
124 GdkWindowState state
= gdk_window_get_state(GDK_WINDOW(widget
->window
));
125 if (state
== GDK_WINDOW_STATE_FULLSCREEN
){
126 gtk_window_unfullscreen(GTK_WINDOW(widget
));
129 gtk_window_fullscreen(GTK_WINDOW(widget
));
134 key_press_event_cb(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
136 g_print("got key %c\n", event
->keyval
);
137 switch (event
->keyval
){
139 toggle_fullscreen(widget
);
142 g_signal_emit_by_name(widget
, "destroy");
151 destroy_cb(GtkWidget
*widget
, gpointer data
)
153 GMainLoop
*loop
= (GMainLoop
*) data
;
154 g_print("Window destroyed\n");
155 g_main_loop_quit(loop
);
156 gtk_widget_destroy(widget
);
160 video_widget_realize_cb(GtkWidget
*widget
, gpointer data
)
162 window_t
*w
= (window_t
*)data
;
163 w
->xid
= GDK_WINDOW_XID(GDK_WINDOW(widget
->window
));
164 g_print("realised window %d with XID %lu\n", w
->id
, w
->xid
);
170 set_up_window(GMainLoop
*loop
, GtkWidget
*window
, int screen_no
){
171 static const GdkColor black
= {0, 0, 0, 0};
172 gtk_window_set_default_size(GTK_WINDOW(window
), option_width
, option_height
);
174 if (option_fullscreen
){
175 gtk_window_fullscreen(GTK_WINDOW(window
));
178 GdkScreen
* screen
= gdk_screen_get_default();
181 if (option_force_multiscreen
){
182 /*Ask gtk to find the approriate monitor. But first warn if
183 the request seems broken. */
184 int monitors
= gdk_screen_get_n_monitors(screen
);
185 if (screen_no
>= monitors
){
186 g_print("Asking for %d out of %d monitors! expect unhappiness!",
187 screen_no
, monitors
);
189 GdkRectangle monitor_shape
;
190 gdk_screen_get_monitor_geometry(screen
, screen_no
, &monitor_shape
);
191 x
= monitor_shape
.x
+ 1;
192 y
= monitor_shape
.y
+ 1;
195 /*simple placement heuristic, places windows evenly across display.
196 This should work with equally sized monitors/projectors, and allows
197 testing on a single monitor. */
198 int width
= gdk_screen_get_width(screen
);
199 x
= (width
/ option_screens
) * screen_no
+ 1;
203 gtk_window_move(GTK_WINDOW(window
), x
, y
);
204 g_print("putting window %d at %d\n", screen_no
, x
);
206 // attach key press signal to key press callback
207 gtk_widget_set_events(window
, GDK_KEY_PRESS_MASK
);
208 g_signal_connect(G_OBJECT(window
), "key-press-event", G_CALLBACK(key_press_event_cb
), NULL
);
209 g_signal_connect(G_OBJECT(window
), "destroy", G_CALLBACK(destroy_cb
), loop
);
211 gtk_widget_modify_bg(window
, GTK_STATE_NORMAL
, &black
);
212 gtk_widget_show_all(window
);
217 gstreamer_start(GMainLoop
*loop
, window_t windows
[MAX_SCREENS
])
219 int input_width
= option_screens
* option_width
;
220 //crop _left/_right are amount to cut, not coordinate of cut
222 int crop_right
= input_width
- option_width
;
224 GstElement
*pipeline
= gst_pipeline_new("e_wha");
225 GstElement
*tee
= pre_tee_pipeline(GST_PIPELINE(pipeline
), input_width
, option_height
);
228 for (i
= 0; i
< option_screens
; i
++){
229 window_t
*w
= windows
+ i
;
230 w
->widget
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
231 w
->sink
= gst_element_factory_make("xvimagesink", NULL
);
233 g_signal_connect(w
->widget
, "realize", G_CALLBACK(video_widget_realize_cb
), w
);
234 set_up_window(loop
, w
->widget
, i
);
235 post_tee_pipeline(GST_PIPELINE(pipeline
), tee
, w
->sink
, crop_left
, crop_right
);
236 crop_left
+= option_width
;
237 crop_right
-= option_width
;
240 GstBus
*bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
241 gst_bus_set_sync_handler(bus
, (GstBusSyncHandler
)sync_bus_call
, windows
);
242 gst_object_unref(bus
);
244 gst_element_set_state(pipeline
, GST_STATE_PLAYING
);
249 gstreamer_stop(GstElement
*pipeline
)
251 gst_element_set_state(pipeline
, GST_STATE_NULL
);
252 gst_object_unref(pipeline
);
255 gint
main (gint argc
, gchar
*argv
[])
257 //initialise threads before any gtk stuff (because not using gtk_init)
258 static window_t windows
[MAX_SCREENS
];
261 /*this is more complicated than plain gtk_init/gst_init, so that options from
262 all over can be gathered and presented together.
264 GOptionGroup
*gst_opts
= gst_init_get_option_group();
265 GOptionGroup
*gtk_opts
= gtk_get_option_group(TRUE
);
266 GOptionContext
*ctx
= g_option_context_new("...!");
267 g_option_context_add_main_entries(ctx
, entries
, NULL
);
268 g_option_context_add_group(ctx
, gst_opts
);
269 g_option_context_add_group(ctx
, gtk_opts
);
270 GError
*error
= NULL
;
271 if (!g_option_context_parse(ctx
, &argc
, &argv
, &error
)){
272 g_print ("Error initializing: %s\n", GST_STR_NULL(error
->message
));
275 g_option_context_free(ctx
);
277 if (option_screens
> MAX_SCREENS
)
278 option_screens
= MAX_SCREENS
;
279 if (option_screens
< MIN_SCREENS
)
280 option_screens
= MIN_SCREENS
;
281 if (option_width
> MAX_PIXELS
)
282 option_width
= MAX_PIXELS
;
283 if (option_height
> MAX_PIXELS
)
284 option_height
= MAX_PIXELS
;
286 GMainLoop
*loop
= g_main_loop_new(NULL
, FALSE
);
288 GstElement
*pipeline
= gstreamer_start(loop
, windows
);
290 g_main_loop_run(loop
);
292 gstreamer_stop(pipeline
);