add filesrc to the pipeline (not quite working)
[opo.git] / gtk-app.c
blob0ecf94e15a72a0591398d254c587bc1b6ac0929c
1 /*N way video splitter */
2 #include <gst/gst.h>
3 #include <gtk/gtk.h>
4 #include <gst/interfaces/xoverlay.h>
5 #include <gdk/gdk.h>
6 #include <gdk/gdkx.h>
7 #include "gtk-app.h"
11 static void
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),
18 "top", 0,
19 "bottom", 0,
20 "left", crop_left,
21 "right", crop_right,
22 NULL);
24 gst_bin_add_many(GST_BIN(pipeline),
25 queue,
26 crop,
27 sink,
28 NULL);
30 gst_element_link_many(tee,
31 queue,
32 crop,
33 sink,
34 NULL);
37 static GstElement *
38 pre_tee_pipeline(GstPipeline *pipeline, int width, int height){
39 if (pipeline == NULL){
40 pipeline = GST_PIPELINE(gst_pipeline_new("wha_pipeline"));
42 GstElement *src;
43 if (option_content) {
44 //src = gst_element_factory_make('uridecodebin', NULL);
45 GstElement *filesrc = gst_element_factory_make("filesrc", NULL);
46 g_object_set(G_OBJECT(filesrc),
47 "location", option_content,
48 NULL);
49 src = gst_element_factory_make("decodebin2", NULL);
50 gst_bin_add_many(GST_BIN(pipeline),
51 filesrc,
52 src,
53 NULL);
54 gst_element_link(filesrc, src);
56 else {
57 char * src_name = (option_fake) ? "videotestsrc" : "v4l2src";
58 src = gst_element_factory_make(src_name, NULL);
59 if (option_fake == 2){//set some properties for an interesting picture
60 g_object_set(G_OBJECT(src),
61 "pattern", 14, //"zone-plate"
62 "kt2", 0,
63 "kx2", 3,
64 "ky2", 3,
65 "kt", 3,
66 "kxy", 2,
67 NULL);
69 gst_bin_add(GST_BIN(pipeline), src);
72 GstElement *tee = gst_element_factory_make ("tee", NULL);
73 GstCaps *caps;
74 caps = gst_caps_new_simple("video/x-raw-yuv",
75 "width", G_TYPE_INT, width,
76 "height", G_TYPE_INT, height,
77 NULL);
78 gst_caps_merge(caps, gst_caps_new_simple("video/x-raw-rgb",
79 "width", G_TYPE_INT, width,
80 "height", G_TYPE_INT, height,
81 NULL));
83 gst_bin_add(GST_BIN(pipeline), tee);
85 gst_element_link_filtered(src,
86 tee,
87 caps);
88 return tee;
92 static void hide_mouse(GtkWidget *widget){
93 GdkWindow *w = GDK_WINDOW(widget->window);
94 GdkDisplay *display = gdk_display_get_default();
95 GdkCursor *cursor = gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR);
96 gdk_window_set_cursor(w, cursor);
97 gdk_cursor_unref (cursor);
102 static GstBusSyncReply
103 sync_bus_call(GstBus *bus, GstMessage *msg, gpointer data)
105 // ignore anything but 'prepare-xwindow-id' element messages
106 if ((GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT) ||
107 (! gst_structure_has_name(msg->structure, "prepare-xwindow-id"))){
108 return GST_BUS_PASS;
110 window_t *windows = (window_t *)data;
111 g_print("Got prepare-xwindow-id msg. \n");
112 //connect this one up with the right window.
113 GstElement *sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
114 int done = 0;
116 g_print("found sink %p\n", sink);
117 for (int i = 0; i < option_screens; i++){
118 const window_t *w = windows + i;
119 if (w->sink == sink){
120 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink), w->xid);
121 g_print("connected sink %d to window %lu\n", i, w->xid);
122 hide_mouse(w->widget);
123 done = 1;
124 break;
128 if (! done){
129 g_print("couldn't find a window for this sink!\n");
132 gst_message_unref(msg);
133 return GST_BUS_DROP;
136 static void
137 toggle_fullscreen(GtkWidget *widget){
138 GdkWindowState state = gdk_window_get_state(GDK_WINDOW(widget->window));
139 if (state == GDK_WINDOW_STATE_FULLSCREEN){
140 gtk_window_unfullscreen(GTK_WINDOW(widget));
142 else{
143 gtk_window_fullscreen(GTK_WINDOW(widget));
147 static gboolean
148 key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
150 g_print("got key %c\n", event->keyval);
151 switch (event->keyval){
152 case 'f':
153 toggle_fullscreen(widget);
154 break;
155 case 'q':
156 g_signal_emit_by_name(widget, "destroy");
157 break;
158 default:
159 break;
161 return TRUE;
164 static void
165 destroy_cb(GtkWidget *widget, gpointer data)
167 GMainLoop *loop = (GMainLoop*) data;
168 g_print("Window destroyed\n");
169 g_main_loop_quit(loop);
170 gtk_widget_destroy(widget);
173 static void
174 video_widget_realize_cb(GtkWidget *widget, gpointer data)
176 window_t *w = (window_t *)data;
177 w->xid = GDK_WINDOW_XID(GDK_WINDOW(widget->window));
178 g_print("realised window %d with XID %lu\n", w->id, w->xid);
179 hide_mouse(widget);
183 static void
184 set_up_window(GMainLoop *loop, window_t *w, int screen_no){
185 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
186 w->widget = window;
187 w->sink = gst_element_factory_make("xvimagesink", NULL);
188 w->id = screen_no;
189 g_signal_connect(w->widget, "realize", G_CALLBACK(video_widget_realize_cb), w);
191 static const GdkColor black = {0, 0, 0, 0};
192 gtk_window_set_default_size(GTK_WINDOW(window), option_width, option_height);
194 if (option_fullscreen){
195 gtk_window_fullscreen(GTK_WINDOW(window));
197 int xscreen_no;
198 GdkScreen * screen;
199 if (option_x_screens <= 1){
200 screen = gdk_screen_get_default();
201 /*Xscreen number might not be 0, but 0 is right assumption for
202 calculations below.*/
203 xscreen_no = 0;
205 else{
206 xscreen_no = screen_no * option_x_screens / option_screens;
207 char display[sizeof(":0.00")];
208 g_snprintf(display, sizeof(display), ":0.%d", xscreen_no);
209 screen = gdk_display_get_screen(gdk_display_get_default(), xscreen_no);
210 g_print("putting window %d on screen %s (%p)\n",
211 screen_no, display, screen);
212 gtk_window_set_screen(GTK_WINDOW(window), screen);
213 g_object_set(G_OBJECT(w->sink),
214 "display", display,
215 NULL);
217 int x, y;
218 int monitors = gdk_screen_get_n_monitors(screen);
219 int monitor_no = screen_no % monitors;
220 if (option_force_multiscreen){
221 /*Ask gtk to find the appropriate monitor (assuming each Xscreen has the
222 same number of monitors).
224 GdkRectangle monitor_shape;
225 gdk_screen_get_monitor_geometry(screen, screen_no, &monitor_shape);
226 x = monitor_shape.x + 1;
227 y = monitor_shape.y + 1;
229 else {
230 /*simple placement heuristic, places windows evenly across display.
231 This should work with equally sized monitors/projectors, and allows
232 testing on a single monitor. */
233 int width = gdk_screen_get_width(screen);
234 x = (width / (option_screens/ option_x_screens)) * monitor_no + 1;
235 y = 50;
238 gtk_window_move(GTK_WINDOW(window), x, y);
239 g_print("putting window %d at %d\n", screen_no, x);
241 // attach key press signal to key press callback
242 gtk_widget_set_events(window, GDK_KEY_PRESS_MASK);
243 g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_event_cb), NULL);
244 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_cb), loop);
246 gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &black);
247 gtk_widget_show_all(window);
248 hide_mouse(window);
251 static GstElement *
252 gstreamer_start(GMainLoop *loop, window_t windows[MAX_SCREENS])
254 int input_width = option_screens * option_width;
255 //crop _left/_right are amount to cut, not coordinate of cut
256 int crop_left = 0;
257 int crop_right = input_width - option_width;
259 GstElement *pipeline = gst_pipeline_new("e_wha");
260 GstElement *tee = pre_tee_pipeline(GST_PIPELINE(pipeline), input_width, option_height);
262 int i;
263 for (i = 0; i < option_screens; i++){
264 window_t *w = windows + i;
265 set_up_window(loop, w, i);
266 post_tee_pipeline(GST_PIPELINE(pipeline), tee, w->sink, crop_left, crop_right);
267 crop_left += option_width;
268 crop_right -= option_width;
271 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
272 gst_bus_set_sync_handler(bus, (GstBusSyncHandler)sync_bus_call, windows);
273 gst_object_unref(bus);
275 gst_element_set_state(pipeline, GST_STATE_PLAYING);
276 return pipeline;
279 static void
280 gstreamer_stop(GstElement *pipeline)
282 gst_element_set_state(pipeline, GST_STATE_NULL);
283 gst_object_unref(pipeline);
286 gint main (gint argc, gchar *argv[])
288 //initialise threads before any gtk stuff (because not using gtk_init)
289 static window_t windows[MAX_SCREENS];
290 g_type_init();
291 g_thread_init(NULL);
292 /*this is more complicated than plain gtk_init/gst_init, so that options from
293 all over can be gathered and presented together.
295 GOptionGroup *gst_opts = gst_init_get_option_group();
296 GOptionGroup *gtk_opts = gtk_get_option_group(TRUE);
297 GOptionContext *ctx = g_option_context_new("...!");
298 g_option_context_add_main_entries(ctx, entries, NULL);
299 g_option_context_add_group(ctx, gst_opts);
300 g_option_context_add_group(ctx, gtk_opts);
301 GError *error = NULL;
302 if (!g_option_context_parse(ctx, &argc, &argv, &error)){
303 g_print ("Error initializing: %s\n", GST_STR_NULL(error->message));
304 exit (1);
306 g_option_context_free(ctx);
307 /*sanitise options*/
308 if (option_x_screens > MAX_X_SCREENS)
309 option_x_screens = MAX_X_SCREENS;
310 if (option_x_screens < MIN_X_SCREENS)
311 option_x_screens = MIN_X_SCREENS;
312 if (option_x_screens > MAX_X_SCREENS)
313 option_screens = MAX_SCREENS;
314 if (option_screens < MIN_SCREENS)
315 option_screens = MIN_SCREENS;
316 if (option_width > MAX_PIXELS)
317 option_width = MAX_PIXELS;
318 if (option_height > MAX_PIXELS)
319 option_height = MAX_PIXELS;
321 GMainLoop *loop = g_main_loop_new(NULL, FALSE);
323 GstElement *pipeline = gstreamer_start(loop, windows);
325 g_main_loop_run(loop);
327 gstreamer_stop(pipeline);
328 return 0;