attempt to add video file playing to gtk-app
[opo.git] / gtk-app.c
blob89678657634bbf575cc0c4d10e6a815b8bdd4799
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);
51 else {
52 char * src_name = (option_fake) ? "videotestsrc" : "v4l2src";
53 src = gst_element_factory_make(src_name, NULL);
54 if (option_fake == 2){//set some properties for an interesting picture
55 g_object_set(G_OBJECT(src),
56 "pattern", 14, //"zone-plate"
57 "kt2", 0,
58 "kx2", 3,
59 "ky2", 3,
60 "kt", 3,
61 "kxy", 2,
62 NULL);
66 GstElement *tee = gst_element_factory_make ("tee", NULL);
67 GstCaps *caps;
68 caps = gst_caps_new_simple("video/x-raw-yuv",
69 "width", G_TYPE_INT, width,
70 "height", G_TYPE_INT, height,
71 NULL);
72 gst_caps_merge(caps, gst_caps_new_simple("video/x-raw-rgb",
73 "width", G_TYPE_INT, width,
74 "height", G_TYPE_INT, height,
75 NULL));
77 gst_bin_add_many(GST_BIN(pipeline),
78 src,
79 tee,
80 NULL);
82 gst_element_link_filtered(src,
83 tee,
84 caps);
85 return tee;
89 static void hide_mouse(GtkWidget *widget){
90 GdkWindow *w = GDK_WINDOW(widget->window);
91 GdkDisplay *display = gdk_display_get_default();
92 GdkCursor *cursor = gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR);
93 gdk_window_set_cursor(w, cursor);
94 gdk_cursor_unref (cursor);
99 static GstBusSyncReply
100 sync_bus_call(GstBus *bus, GstMessage *msg, gpointer data)
102 // ignore anything but 'prepare-xwindow-id' element messages
103 if ((GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT) ||
104 (! gst_structure_has_name(msg->structure, "prepare-xwindow-id"))){
105 return GST_BUS_PASS;
107 window_t *windows = (window_t *)data;
108 g_print("Got prepare-xwindow-id msg. \n");
109 //connect this one up with the right window.
110 GstElement *sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
111 int done = 0;
113 g_print("found sink %p\n", sink);
114 for (int i = 0; i < option_screens; i++){
115 const window_t *w = windows + i;
116 if (w->sink == sink){
117 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink), w->xid);
118 g_print("connected sink %d to window %lu\n", i, w->xid);
119 hide_mouse(w->widget);
120 done = 1;
121 break;
125 if (! done){
126 g_print("couldn't find a window for this sink!\n");
129 gst_message_unref(msg);
130 return GST_BUS_DROP;
133 static void
134 toggle_fullscreen(GtkWidget *widget){
135 GdkWindowState state = gdk_window_get_state(GDK_WINDOW(widget->window));
136 if (state == GDK_WINDOW_STATE_FULLSCREEN){
137 gtk_window_unfullscreen(GTK_WINDOW(widget));
139 else{
140 gtk_window_fullscreen(GTK_WINDOW(widget));
144 static gboolean
145 key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
147 g_print("got key %c\n", event->keyval);
148 switch (event->keyval){
149 case 'f':
150 toggle_fullscreen(widget);
151 break;
152 case 'q':
153 g_signal_emit_by_name(widget, "destroy");
154 break;
155 default:
156 break;
158 return TRUE;
161 static void
162 destroy_cb(GtkWidget *widget, gpointer data)
164 GMainLoop *loop = (GMainLoop*) data;
165 g_print("Window destroyed\n");
166 g_main_loop_quit(loop);
167 gtk_widget_destroy(widget);
170 static void
171 video_widget_realize_cb(GtkWidget *widget, gpointer data)
173 window_t *w = (window_t *)data;
174 w->xid = GDK_WINDOW_XID(GDK_WINDOW(widget->window));
175 g_print("realised window %d with XID %lu\n", w->id, w->xid);
176 hide_mouse(widget);
180 static void
181 set_up_window(GMainLoop *loop, window_t *w, int screen_no){
182 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
183 w->widget = window;
184 w->sink = gst_element_factory_make("xvimagesink", NULL);
185 w->id = screen_no;
186 g_signal_connect(w->widget, "realize", G_CALLBACK(video_widget_realize_cb), w);
188 static const GdkColor black = {0, 0, 0, 0};
189 gtk_window_set_default_size(GTK_WINDOW(window), option_width, option_height);
191 if (option_fullscreen){
192 gtk_window_fullscreen(GTK_WINDOW(window));
194 int xscreen_no;
195 GdkScreen * screen;
196 if (option_x_screens <= 1){
197 screen = gdk_screen_get_default();
198 /*Xscreen number might not be 0, but 0 is right assumption for
199 calculations below.*/
200 xscreen_no = 0;
202 else{
203 xscreen_no = screen_no * option_x_screens / option_screens;
204 char display[sizeof(":0.00")];
205 g_snprintf(display, sizeof(display), ":0.%d", xscreen_no);
206 screen = gdk_display_get_screen(gdk_display_get_default(), xscreen_no);
207 g_print("putting window %d on screen %s (%p)\n",
208 screen_no, display, screen);
209 gtk_window_set_screen(GTK_WINDOW(window), screen);
210 g_object_set(G_OBJECT(w->sink),
211 "display", display,
212 NULL);
214 int x, y;
215 int monitors = gdk_screen_get_n_monitors(screen);
216 int monitor_no = screen_no % monitors;
217 if (option_force_multiscreen){
218 /*Ask gtk to find the appropriate monitor (assuming each Xscreen has the
219 same number of monitors).
221 GdkRectangle monitor_shape;
222 gdk_screen_get_monitor_geometry(screen, screen_no, &monitor_shape);
223 x = monitor_shape.x + 1;
224 y = monitor_shape.y + 1;
226 else {
227 /*simple placement heuristic, places windows evenly across display.
228 This should work with equally sized monitors/projectors, and allows
229 testing on a single monitor. */
230 int width = gdk_screen_get_width(screen);
231 x = (width / (option_screens/ option_x_screens)) * monitor_no + 1;
232 y = 50;
235 gtk_window_move(GTK_WINDOW(window), x, y);
236 g_print("putting window %d at %d\n", screen_no, x);
238 // attach key press signal to key press callback
239 gtk_widget_set_events(window, GDK_KEY_PRESS_MASK);
240 g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_event_cb), NULL);
241 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_cb), loop);
243 gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &black);
244 gtk_widget_show_all(window);
245 hide_mouse(window);
248 static GstElement *
249 gstreamer_start(GMainLoop *loop, window_t windows[MAX_SCREENS])
251 int input_width = option_screens * option_width;
252 //crop _left/_right are amount to cut, not coordinate of cut
253 int crop_left = 0;
254 int crop_right = input_width - option_width;
256 GstElement *pipeline = gst_pipeline_new("e_wha");
257 GstElement *tee = pre_tee_pipeline(GST_PIPELINE(pipeline), input_width, option_height);
259 int i;
260 for (i = 0; i < option_screens; i++){
261 window_t *w = windows + i;
262 set_up_window(loop, w, i);
263 post_tee_pipeline(GST_PIPELINE(pipeline), tee, w->sink, crop_left, crop_right);
264 crop_left += option_width;
265 crop_right -= option_width;
268 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
269 gst_bus_set_sync_handler(bus, (GstBusSyncHandler)sync_bus_call, windows);
270 gst_object_unref(bus);
272 gst_element_set_state(pipeline, GST_STATE_PLAYING);
273 return pipeline;
276 static void
277 gstreamer_stop(GstElement *pipeline)
279 gst_element_set_state(pipeline, GST_STATE_NULL);
280 gst_object_unref(pipeline);
283 gint main (gint argc, gchar *argv[])
285 //initialise threads before any gtk stuff (because not using gtk_init)
286 static window_t windows[MAX_SCREENS];
287 g_type_init();
288 g_thread_init(NULL);
289 /*this is more complicated than plain gtk_init/gst_init, so that options from
290 all over can be gathered and presented together.
292 GOptionGroup *gst_opts = gst_init_get_option_group();
293 GOptionGroup *gtk_opts = gtk_get_option_group(TRUE);
294 GOptionContext *ctx = g_option_context_new("...!");
295 g_option_context_add_main_entries(ctx, entries, NULL);
296 g_option_context_add_group(ctx, gst_opts);
297 g_option_context_add_group(ctx, gtk_opts);
298 GError *error = NULL;
299 if (!g_option_context_parse(ctx, &argc, &argv, &error)){
300 g_print ("Error initializing: %s\n", GST_STR_NULL(error->message));
301 exit (1);
303 g_option_context_free(ctx);
304 /*sanitise options*/
305 if (option_x_screens > MAX_X_SCREENS)
306 option_x_screens = MAX_X_SCREENS;
307 if (option_x_screens < MIN_X_SCREENS)
308 option_x_screens = MIN_X_SCREENS;
309 if (option_x_screens > MAX_X_SCREENS)
310 option_screens = MAX_SCREENS;
311 if (option_screens < MIN_SCREENS)
312 option_screens = MIN_SCREENS;
313 if (option_width > MAX_PIXELS)
314 option_width = MAX_PIXELS;
315 if (option_height > MAX_PIXELS)
316 option_height = MAX_PIXELS;
318 GMainLoop *loop = g_main_loop_new(NULL, FALSE);
320 GstElement *pipeline = gstreamer_start(loop, windows);
322 g_main_loop_run(loop);
324 gstreamer_stop(pipeline);
325 return 0;