From a3a6a3d3fe783c0fb3900d7ac8000dac3e447516 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Tue, 11 Jan 2011 16:03:23 +1300 Subject: [PATCH] Add C player basedon sparrow gtk-app --- Makefile | 69 +++++++++++++++++++++++++ gtk-app.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gtk-app.h | 76 +++++++++++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 Makefile create mode 100644 gtk-app.c create mode 100644 gtk-app.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2ede4fe --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +all:: + +GDB_ALWAYS_FLAGS = -g +WARNINGS = -Wall -Wextra -Wno-unused-parameter + +ARCH = $(shell arch) +ifeq "$(ARCH)" "x86_64" +ARCH_CFLAGS = -fPIC -DPIC -m64 +else +ARCH_CFLAGS = -m32 -msse2 +endif + +ALL_CFLAGS = -march=native -pthread $(VECTOR_FLAGS) -O3 $(WARNINGS) -pipe -D_GNU_SOURCE -std=gnu99 $(INCLUDES) $(ARCH_CFLAGS) $(CFLAGS) $(GDB_ALWAYS_FLAGS) +ALL_LDFLAGS = $(LDFLAGS) + +VECTOR_FLAGS = -msse2 -DHAVE_SSE2 -D__SSE2__ -floop-strip-mine -floop-block + +# these *might* do something useful +# -fvisibility=hidden +#POSSIBLE_OPTIMISING_CFLAGS = -fmodulo-sched -fmodulo-sched-allow-regmoves -fgcse-sm -fgcse-las \ +# -funsafe-loop-optimizations -Wunsafe-loop-optimizations -fsee -funsafe-math-optimizations and more +# "-combine -fwhole-program" with __attribute__((externally_visible)) +# -fprofile-arcs and -fbranch-probabilities +#POSSIBLE_PESSIMISING_CFLAGS -fmudflap -fmudflapth -fmudflapir + +CC = gcc +AR = ar +INSTALL = install + +GST_INCLUDES = -I/usr/include/gstreamer-0.10 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/libxml2 +INCLUDES = -I. $(GST_INCLUDES) + +LINKS = -L/usr/local/lib -lgstbase-0.10 -lgstreamer-0.10 -lgobject-2.0 \ + -lglib-2.0 -lgstvideo-0.10 + +SOURCES = +OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) + +clean: + rm -f *.so *.o *.a *.d *.s + +.c.o: +# @echo $(CPATH) +# @echo $(LIBRARY_PATH) + $(CC) -c -MD $(ALL_CFLAGS) $(CPPFLAGS) -o $@ $< +# $(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) -MD $< + +%.s: %.c + $(CC) -S $(ALL_CFLAGS) $(CPPFLAGS) -o $@ $< + +%.i: %.c + $(CC) -E $(ALL_CFLAGS) $(CPPFLAGS) -o $@ $< + + +test-gtk: debug gtk-app + GST_DEBUG=sparrow:$(DEBUG_LEVEL) gdb ./gtk-app + +debug: + make -B CFLAGS='-g -fno-inline -fno-inline-functions -fno-omit-frame-pointer' + +.PHONY: TAGS all rsync app-clean clean + +GTK_APP = gtk-app.c +GTK_LINKS = -lglib-2.0 $(LINKS) -lgstinterfaces-0.10 -lgtk-x11-2.0 +GTK_INCLUDES = -I/usr/include/gtk-2.0/ -I/usr/include/cairo/ -I/usr/include/pango-1.0/ -I/usr/lib/gtk-2.0/include/ -I/usr/include/atk-1.0/ -I/usr/include/gdk-pixbuf-2.0/ + +gtk-app:: + $(CC) -g $(ALL_CFLAGS) $(CPPFLAGS) $(CV_LINKS) $(INCLUDES) $(GTK_INCLUDES)\ + $(GTK_LINKS) -o $@ $(GTK_APP) \ No newline at end of file diff --git a/gtk-app.c b/gtk-app.c new file mode 100644 index 0000000..0f6268b --- /dev/null +++ b/gtk-app.c @@ -0,0 +1,173 @@ +/*4 way video splitter */ +#include +#include +#include +#include +#include +#include "gtk-app.h" + +static GstPipeline * +make_multi_pipeline(windows_t *windows, int count) +{ + GstPipeline *pipeline = GST_PIPELINE(gst_pipeline_new("e_wha")); + GstElement *tee = pre_tee_pipeline(pipeline); + int i; + for (i = 0; i < count; i++){ + GstElement *sink = windows->sinks[i]; + post_tee_pipeline(pipeline, tee, sink, i); + } + return pipeline; +} + + +static void +bus_call(GstBus * bus, GstMessage *msg, gpointer data) +{ + windows_t *windows = (windows_t *)data; + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(msg->structure, "prepare-xwindow-id")){ + g_print("Got prepare-xwindow-id msg. option screens: %d\n", option_screens); + for (int i = 0; i < option_screens; i++){ + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(windows->sinks[i]), + windows->xwindows[i]); + g_print("connected sink %d to window %lu\n", i, windows->xwindows[i]); + hide_mouse(windows->gtk_windows[i]); + } + } +} + +static void +toggle_fullscreen(GtkWidget *widget){ + GdkWindowState state = gdk_window_get_state(GDK_WINDOW(widget->window)); + if (state == GDK_WINDOW_STATE_FULLSCREEN){ + gtk_window_unfullscreen(GTK_WINDOW(widget)); + } + else{ + gtk_window_fullscreen(GTK_WINDOW(widget)); + } +} + +static gboolean +key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + g_print("got key %c\n", event->keyval); + switch (event->keyval){ + case 'f': + toggle_fullscreen(widget); + break; + case 'q': + g_signal_emit_by_name(widget, "destroy"); + break; + default: + break; + } + return TRUE; +} + +void destroy_cb(GtkWidget * widget, gpointer data) +{ + GMainLoop *loop = (GMainLoop*) data; + g_print("Window destroyed\n"); + g_main_loop_quit(loop); +} + +static void +video_widget_realize_cb(GtkWidget *widget, gpointer data) +{ + windows_t *windows = (windows_t *)data; + int r = windows->realised; + if (r < MAX_SCREENS){ + windows->xwindows[r] = GDK_WINDOW_XID(GDK_WINDOW(widget->window)); + g_print("realised window %d with XID %lu\n", r, windows->xwindows[r]); + } + else { + g_print("wtf, there seem to be %d windows!\n", r); + } + windows->realised++; + hide_mouse(widget); +} + + +static void +set_up_window(GMainLoop *loop, GtkWidget *window, int screen_no){ + static const GdkColor black = {0, 0, 0, 0}; + gtk_window_set_default_size(GTK_WINDOW(window), WIDTH, HEIGHT); + + if (option_fullscreen){ + gtk_window_fullscreen(GTK_WINDOW(window)); + } + + /*if more than one screen is requested, set the screen number. + otherwise let it fall were it falls */ + if (option_screens > 1){ + GdkScreen * screen = gdk_screen_get_default(); + int width = gdk_screen_get_width(screen); + /* XXX placement heuristic is crap */ + gtk_window_move(GTK_WINDOW(window), + (width / 2 * screen_no + 200) % width, 50); + } + + // attach key press signal to key press callback + gtk_widget_set_events(window, GDK_KEY_PRESS_MASK); + g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_event_cb), NULL); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_cb), loop); + + gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &black); + gtk_widget_show_all(window); + hide_mouse(window); +} + + +gint main (gint argc, gchar *argv[]) +{ + //initialise threads before any gtk stuff (because not using gtk_init) + g_thread_init(NULL); + /*this is more complicated than plain gtk_init/gst_init, so that options from + all over can be gathered and presented together. + */ + GOptionGroup *gst_opts = gst_init_get_option_group(); + GOptionGroup *gtk_opts = gtk_get_option_group(TRUE); + GOptionContext *ctx = g_option_context_new("...!"); + g_option_context_add_main_entries(ctx, entries, NULL); + g_option_context_add_group(ctx, gst_opts); + g_option_context_add_group(ctx, gtk_opts); + GError *error = NULL; + if (!g_option_context_parse(ctx, &argc, &argv, &error)){ + g_print ("Error initializing: %s\n", GST_STR_NULL(error->message)); + exit (1); + } + g_option_context_free(ctx); + + GMainLoop *loop = g_main_loop_new(NULL, FALSE); + + windows_t windows; + windows.realised = 0; + + int i; + for (i = 0; i < option_screens; i++){ + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_signal_connect(window, "realize", + G_CALLBACK(video_widget_realize_cb), &windows); + /* set up sink here */ + GstElement *sink = gst_element_factory_make("ximagesink", NULL); + set_up_window(loop, window, i); + windows.gtk_windows[i] = window; + windows.sinks[i] = sink; + } + + GstElement *pipeline = (GstElement *)make_multi_pipeline(&windows, option_screens); + + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + gst_bus_add_watch(bus, (GstBusFunc)bus_call, &windows); + gst_object_unref(bus); + + gst_element_set_state(pipeline, GST_STATE_PLAYING); + + g_main_loop_run(loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + return 0; +} + + diff --git a/gtk-app.h b/gtk-app.h new file mode 100644 index 0000000..e0b9202 --- /dev/null +++ b/gtk-app.h @@ -0,0 +1,76 @@ +#define WIDTH 400 +#define HEIGHT 300 + +#define QUOTE_(x) #x +#define QUOTE(x) QUOTE_(x) + +static gboolean option_fake = TRUE; /* Should eventually be FALSE !*/ +static gboolean option_fullscreen = FALSE; +static gint option_screens = 1; + +#define MAX_SCREENS 8 + +static GOptionEntry entries[] = +{ + { "fake-source", 0, 0, G_OPTION_ARG_NONE, &option_fake, + "use videotestsrc", NULL }, + { "full-screen", 'f', 0, G_OPTION_ARG_NONE, &option_fullscreen, "run full screen", NULL }, + { "screens", 's', 0, G_OPTION_ARG_INT, &option_screens, "Use this many screens, (max " + QUOTE(MAX_SCREENS) ")", "S" }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + + +typedef struct windows_s { + int realised; + int requested; + GstElement *sinks[MAX_SCREENS]; + XID xwindows[MAX_SCREENS]; + GtkWidget *gtk_windows[MAX_SCREENS]; +} windows_t; + + + +static void +post_tee_pipeline(GstPipeline *pipeline, GstElement *tee, GstElement *sink, + int id){ + GstElement *queue = gst_element_factory_make("queue", NULL); + gst_bin_add_many (GST_BIN(pipeline), + queue, + sink, + NULL); + + gst_element_link_many(tee, + queue, + sink, + NULL); +} + +static GstElement * +pre_tee_pipeline(GstPipeline *pipeline){ + if (pipeline == NULL){ + pipeline = GST_PIPELINE(gst_pipeline_new("wha_pipeline")); + } + char * src_name = (option_fake) ? "videotestsrc" : "v4l2src"; + GstElement *src = gst_element_factory_make(src_name, NULL); + GstElement *tee = gst_element_factory_make ("tee", NULL); + + gst_bin_add_many(GST_BIN(pipeline), + src, + tee, + NULL); + + gst_element_link_many(src, + tee, + NULL); + return tee; +} + + +static void hide_mouse(GtkWidget *widget){ + GdkWindow *w = GDK_WINDOW(widget->window); + GdkDisplay *display = gdk_display_get_default(); + GdkCursor *cursor = gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR); + gdk_window_set_cursor(w, cursor); + gdk_cursor_unref (cursor); +} -- 2.11.4.GIT