Import stuff from old repo
authorMikko Rasa <mikko.rasa@movial.fi>
Tue, 21 Oct 2008 10:09:05 +0000 (13:09 +0300)
committerMikko Rasa <mikko.rasa@movial.fi>
Tue, 21 Oct 2008 10:09:05 +0000 (13:09 +0300)
30 files changed:
.gitignore [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
data/Makefile.am [new file with mode: 0644]
data/com.ixs.octopus.service.in [new file with mode: 0644]
data/gstreamer.ocd [new file with mode: 0644]
data/octopus.conf [new file with mode: 0644]
data/octopus.pc.in [new file with mode: 0644]
modules/Makefile.am [new file with mode: 0644]
player/Makefile.am [new file with mode: 0644]
player/main.c [new file with mode: 0644]
player/octopus-player.c [new file with mode: 0644]
player/octopus-player.h [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/octopus-backend-gst.c [new file with mode: 0644]
src/octopus-backend-gst.h [new file with mode: 0644]
src/octopus-backend.c [new file with mode: 0644]
src/octopus-backend.h [new file with mode: 0644]
src/octopus-dbus.xml [new file with mode: 0644]
src/octopus-marshals.spec [new file with mode: 0644]
src/octopus-module-manager.c [new file with mode: 0644]
src/octopus-module-manager.h [new file with mode: 0644]
src/octopus-module.c [new file with mode: 0644]
src/octopus-module.h [new file with mode: 0644]
src/octopus-route.c [new file with mode: 0644]
src/octopus-route.h [new file with mode: 0644]
src/octopus-server.c [new file with mode: 0644]
src/octopus-server.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ee338ca
--- /dev/null
@@ -0,0 +1,31 @@
+*.o
+data/com.ixs.octopus.service
+data/octopus.pc
+src/octopus
+src/octopus-glue.h
+src/octopus-marshals.h
+src/octopus-marshals.c
+player/octopus-player
+player/octopus-dbus.h
+player/octopus-marshals.h
+player/octopus-marshals.c
+
+# Matrix stuff
+meta/
+stamp-h1
+
+# Big buncha crap by autotools
+.deps/
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+compile
+config.*
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..d970737
--- /dev/null
@@ -0,0 +1,7 @@
+if PLAYER
+PLAYER_DIR = player
+endif
+
+SUBDIRS = src data $(PLAYER_DIR)
+
+EXTRA_DIST = autogen.sh
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..63c29eb
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -x
+libtoolize --automake
+aclocal-1.7 || aclocal
+autoconf
+autoheader
+automake-1.7 --add-missing --foreign || automake --add-missing --foreign
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..2dffb4b
--- /dev/null
@@ -0,0 +1,70 @@
+AC_PREREQ(2.52)
+AC_INIT(octopus, 0.1)
+AC_CONFIG_SRCDIR(src/main.c)
+AM_CONFIG_HEADER(config.h)
+
+AM_INIT_AUTOMAKE
+
+AC_PROG_CC
+AC_PROG_LIBTOOL
+
+CFLAGS="$CFLAGS -Wall -Werror -g -Wmissing-prototypes -Wmissing-declarations -std=gnu99"
+
+dnl -----------------------
+PKG_CHECK_MODULES(glib, [glib-2.0 >= 2.4], , AC_MSG_ERROR(glib >= 2.4 is required))
+AC_SUBST(glib_CFLAGS)
+AC_SUBST(glib_LIBS)
+
+PKG_CHECK_MODULES(dbus, [dbus-1], , AC_MSG_ERROR(dbus >= 1.0 is required))
+AC_SUBST(dbus_CFLAGS)
+AC_SUBST(dbus_LIBS)
+
+PKG_CHECK_MODULES(dbus_glib, [dbus-glib-1], , AC_MSG_ERROR(dbus-glib >= 1.0 is required))
+AC_SUBST(dbus_glib_CFLAGS)
+AC_SUBST(dbus_glib_LIBS)
+
+PKG_CHECK_MODULES(gmodule, [gmodule-2.0], , AC_MSG_ERROR(gmodule >= 2.0 is required))
+AC_SUBST(gmodule_CFLAGS)
+AC_SUBST(gmodule_LIBS)
+
+PKG_CHECK_MODULES(gnome_vfs, [gnome-vfs-2.0], , AC_MSG_ERROR(gnome-vfs >= 2.0 is required))
+AC_SUBST(gnome_vfs_CFLAGS)
+AC_SUBST(gnome_vfs_LIBS)
+
+PKG_CHECK_MODULES(gstreamer, [gstreamer-0.10 >= 0.10.10], , AC_MSG_ERROR(gstreamer-0.10 >= 0.10.10 is required))
+AC_SUBST(gstreamer_CFLAGS)
+AC_SUBST(gstreamer_LIBS)
+
+dnl --- MODULES -----------
+
+MODULEDIR='${libdir}/octopus'
+AC_SUBST(MODULEDIR)
+COMPONENTDIR='${datadir}/octopus'
+AC_SUBST(COMPONENTDIR)
+
+dnl --- Player ------------
+
+AC_ARG_ENABLE(player, AS_HELP_STRING([--enable-player], [Build a simple GTK frontend]), player=$enableval, player=no)
+AM_CONDITIONAL(PLAYER, test x$player = xyes)
+if test "$player" = yes; then
+       PKG_CHECK_MODULES(gtk, [gtk+-2.0 >= 2.4], , AC_MSG_ERROR(gtk+ >= 2.4 is required))
+       AC_SUBST(gtk_CFLAGS)
+       AC_SUBST(gtk_LIBS)
+fi
+
+dnl --- Service files -----
+
+
+dnl --- Compiling flags ---
+
+dnl -----------------------
+
+
+AC_OUTPUT(
+Makefile
+src/Makefile
+modules/Makefile
+data/Makefile
+data/octopus.pc
+player/Makefile
+)
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644 (file)
index 0000000..fc92fab
--- /dev/null
@@ -0,0 +1,17 @@
+dbusservicedir = $(datadir)/dbus-1/services
+dbusservice_DATA = com.ixs.octopus.service
+
+dbusrulesdir = $(sysconfdir)/dbus-1/system.d
+dbusrules_DATA = octopus.conf
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = octopus.pc
+
+componentdir = $(datadir)/octopus
+component_DATA = gstreamer.ocd
+
+EXTRA_DIST = com.ixs.octopus.service.in octopus.pc.in gstreamer.ocd
+CLEANFILES = com.ixs.octopus.service octopus.pc
+
+%.service: %.service.in Makefile
+       sed -e 's![@]exec_prefix[@]!$(exec_prefix)!' $< > $@
diff --git a/data/com.ixs.octopus.service.in b/data/com.ixs.octopus.service.in
new file mode 100644 (file)
index 0000000..c81b2d3
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=com.ixs.OctopusService
+Exec=@exec_prefix@/bin/octopus
diff --git a/data/gstreamer.ocd b/data/gstreamer.ocd
new file mode 100644 (file)
index 0000000..f81514d
--- /dev/null
@@ -0,0 +1,55 @@
+# Octopus Component Definitions for GStreamer backend
+
+component src_gnomevfs
+       element gnomevfssrc
+               source local-file-gnomevfs
+       element typefind
+       dynamic
+
+component demux_oggdemux
+       element oggdemux
+       dynamic
+
+component dec_ivorbis
+       element ivorbisdec
+
+component dec_vorbis
+       element vorbisdec
+
+component demux_id3demux
+       element id3demux
+       dynamic
+
+component dec_mp3mad
+       element mp3parse
+       element mad
+
+component sink_alsasink
+       element audioconvert
+       element alsasink
+               sink local-audio-alsa
+
+component demux_avidemux
+       element avidemux
+       dynamic
+
+component dec_ffmpeg_mpeg4
+       element ffdec_mpeg4
+
+component sink_xvimagesink
+       priority 1
+       element xvimagesink
+               parameter force-aspect-ratio true
+               sink local-video-xvimage
+
+component sink_ximagesink
+       element ffmpegcolorspace
+       element videoscale
+       element ximagesink
+               parameter force-aspect-ratio true
+               sink local-video-ximage
+
+# Fakesink causes b0rkage by attaching to inappropriate places
+#component sink_fakesink
+#      element fakesink
+#              sink local-any-fakesink
diff --git a/data/octopus.conf b/data/octopus.conf
new file mode 100644 (file)
index 0000000..c902936
--- /dev/null
@@ -0,0 +1,20 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+        <policy user="aeki">
+                <allow own="com.ixs.OctopusService" />
+                <allow send_destination="com.ixs.OctopusService" />
+               <allow send_interface="com.ixs.OctopusService.SetUri" />
+               <allow send_interface="com.ixs.OctopusService.Play" />
+               <allow send_interface="com.ixs.OctopusService.New" />
+               <allow send_interface="com.ixs.OctopusService.Release" />
+        </policy>
+        <policy context="default">
+                <allow own="com.ixs.OctopusService" />
+               <allow send_interface="com.ixs.OctopusService.SetUri" />
+               <allow send_interface="com.ixs.OctopusService.Play" />
+                <allow send_interface="com.ixs.OctopusService.New" />
+                <allow send_interface="com.ixs.OctopusService.Release" />
+        </policy>
+</busconfig>
diff --git a/data/octopus.pc.in b/data/octopus.pc.in
new file mode 100644 (file)
index 0000000..326206f
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+moduledir=@MODULEDIR@
+
+Name: octopus
+Description: Octopus.
+Requires: glib-2.0 gmodule-2.0
+Version: @VERSION@
+Libs: -L${libdir} @glib_LIBS@ @gmodule_LIBS@
+Cflags: -I${includedir} @glib_CFLAGS@ @gmodule_CFLAGS@
diff --git a/modules/Makefile.am b/modules/Makefile.am
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/player/Makefile.am b/player/Makefile.am
new file mode 100644 (file)
index 0000000..eff3f9c
--- /dev/null
@@ -0,0 +1,23 @@
+bin_PROGRAMS = octopus-player
+
+octopus_player_SOURCES = main.c \
+       octopus-player.c \
+       octopus-marshals.c
+
+AM_CFLAGS = $(gtk_CFLAGS) $(dbus_CFLAGS) $(dbus_glib_CFLAGS)
+
+octopus_player_LDADD = $(gtk_LIBS) $(dbus_LIBS) $(dbus_glib_LIBS)
+
+BUILT_SOURCES = octopus-dbus.h \
+       octopus-marshals.h \
+       octopus-marshals.c
+
+octopus-dbus.h: ../src/octopus-dbus.xml
+       dbus-binding-tool --prefix=octopus_server --mode=glib-client --output=$@ $<
+
+octopus-marshals.h: ../src/octopus-marshals.spec
+       glib-genmarshal --header $< >$@
+
+octopus-marshals.c: ../src/octopus-marshals.spec
+       echo '#include "octopus-marshals.h"' >$@
+       glib-genmarshal --body $< >>$@
diff --git a/player/main.c b/player/main.c
new file mode 100644 (file)
index 0000000..4a632fa
--- /dev/null
@@ -0,0 +1,36 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <gtk/gtk.h>
+#include "octopus-player.h"
+
+int main(int argc, char **argv)
+{
+       OctopusPlayer *player;
+
+       gtk_init(&argc, &argv);
+
+       player=OCTOPUS_PLAYER(g_object_new(OCTOPUS_PLAYER_TYPE, NULL));
+
+       gtk_main();
+
+       g_object_unref(player);
+
+       return 0;
+}
diff --git a/player/octopus-player.c b/player/octopus-player.c
new file mode 100644 (file)
index 0000000..c3d17ff
--- /dev/null
@@ -0,0 +1,245 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include "octopus-player.h"
+#include "octopus-dbus.h"
+#include "octopus-marshals.h"
+
+struct _OctopusPlayerPrivate
+{
+       DBusGConnection *dbus_conn;
+       DBusGProxy *octopus_proxy;
+
+       guint route_id;
+
+       GtkWindow  *toplevel;
+       GtkWidget  *video_area;
+       GtkWidget  *title_label;
+       GtkWidget  *progress;
+};
+
+static void octopus_player_finalize(GObject *player);
+void create_route(OctopusPlayer *player);
+static void open_pressed(GtkWidget *button, gpointer user_data);
+static void play_pressed(GtkWidget *button, gpointer user_data);
+static void pause_pressed(GtkWidget *button, gpointer user_data);
+static void stop_pressed(GtkWidget *button, gpointer user_data);
+static void quit_pressed(GtkWidget *button, gpointer);
+static void playback_error(DBusGProxy *proxy, guint id, gchar *error, gpointer user_data);
+static void stream_position(DBusGProxy *proxy, guint id, guint pos, guint dur, gpointer user_data);
+
+G_DEFINE_TYPE(OctopusPlayer, octopus_player, G_TYPE_OBJECT)
+
+static void octopus_player_class_init(OctopusPlayerClass *klass)
+{
+       GObjectClass *gobject_class;
+       
+       gobject_class = G_OBJECT_CLASS(klass);
+
+       gobject_class->finalize = octopus_player_finalize;
+}
+
+static void octopus_player_init(OctopusPlayer *player)
+{
+       OctopusPlayerPrivate *priv;
+       GtkWidget *vbox;
+       GtkWidget *hbox;
+       GtkWidget *button;
+
+       priv = player->priv = g_new0(OctopusPlayerPrivate, 1);
+
+       priv->toplevel = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+       gtk_window_set_default_size(priv->toplevel, 800, 480);
+
+       vbox = gtk_vbox_new(FALSE, 4);
+       gtk_container_add(GTK_CONTAINER(priv->toplevel), vbox);
+
+       priv->video_area = gtk_drawing_area_new();
+       gtk_box_pack_start(GTK_BOX(vbox), priv->video_area, TRUE, TRUE, 0);
+
+       priv->title_label = gtk_label_new("");
+       gtk_box_pack_start(GTK_BOX(vbox), priv->title_label, FALSE, FALSE, 0);
+
+       priv->progress = gtk_progress_bar_new();
+       gtk_box_pack_start(GTK_BOX(vbox), priv->progress, FALSE, FALSE, 0);
+       gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progress), "-:-- / -:--");
+
+       hbox = gtk_hbox_new(FALSE, 4);
+       gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+       gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+
+       button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+       gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+       g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_pressed), player);
+
+       button = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
+       gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+       g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(play_pressed), player);
+
+       button = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PAUSE);
+       gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+       g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pause_pressed), player);
+
+       button = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
+       gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+       g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(stop_pressed), player);
+
+       button = gtk_button_new_from_stock(GTK_STOCK_QUIT);
+       gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+       g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(quit_pressed), player);
+
+       gtk_widget_show_all(GTK_WIDGET(priv->toplevel));
+
+       priv->dbus_conn=dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+       priv->octopus_proxy=dbus_g_proxy_new_for_name(priv->dbus_conn, "com.ixs.OctopusService", "/com/ixs/OctopusService", "com.ixs.OctopusService");
+
+       dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID);
+       dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__UINT_STRING, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID);
+       dbus_g_proxy_add_signal(priv->octopus_proxy, "PlaybackError", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID);
+       dbus_g_proxy_connect_signal(priv->octopus_proxy, "PlaybackError", G_CALLBACK(playback_error), player, NULL);
+       dbus_g_proxy_add_signal(priv->octopus_proxy, "StreamPosition", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID);
+       dbus_g_proxy_connect_signal(priv->octopus_proxy, "StreamPosition", G_CALLBACK(stream_position), player, NULL);
+}
+
+static void octopus_player_finalize(GObject *object)
+{
+       OctopusPlayer        *player = (OctopusPlayer *)object;
+       OctopusPlayerPrivate *priv = player->priv;
+
+       if(priv->route_id)
+               com_ixs_OctopusService_release(priv->octopus_proxy, priv->route_id, NULL);
+       dbus_g_connection_unref(priv->dbus_conn);
+
+       gtk_object_destroy(GTK_OBJECT(priv->toplevel));
+       g_free(priv);
+}
+
+/* Helper functions */
+
+void create_route(OctopusPlayer *player)
+{
+       OctopusPlayerPrivate *priv = player->priv;
+       static const gchar   *sources[] = { "local-file", NULL };
+       static const gchar   *sinks[] = { "local-audio", "local-video", NULL };
+       GdkWindow            *wnd;
+       GdkDisplay           *dpy;
+
+       if(priv->route_id)
+               com_ixs_OctopusService_release(player->priv->octopus_proxy, priv->route_id, NULL);
+
+       com_ixs_OctopusService_new(priv->octopus_proxy, &priv->route_id, NULL);
+       com_ixs_OctopusService_set_mapping(priv->octopus_proxy, priv->route_id, sources, sinks, NULL);
+       /* gtk_widget_get_window came in GTK+ 2.14 */
+       wnd = priv->video_area->window;
+       dpy = gdk_drawable_get_display(GDK_DRAWABLE(wnd));
+       com_ixs_OctopusService_set_x11_display(priv->octopus_proxy, priv->route_id, "local-video",
+               gdk_display_get_name(dpy), gdk_x11_drawable_get_xid(GDK_DRAWABLE(wnd)), NULL);
+}
+
+/* UI event handlers */
+
+static void open_response(GtkDialog *dialog, gint resp_id, gpointer user_data)
+{
+       OctopusPlayer        *player = (OctopusPlayer *)user_data;
+       OctopusPlayerPrivate *priv = player->priv;
+       GtkFileChooser       *chooser;
+       gchar                *uri;
+
+       chooser = GTK_FILE_CHOOSER(dialog);
+       uri = gtk_file_chooser_get_uri(chooser);
+       gtk_label_set_text(GTK_LABEL(priv->title_label), uri);
+
+       create_route(player);
+       com_ixs_OctopusService_set_endpoint_uri(priv->octopus_proxy, priv->route_id, "local-file", uri, NULL);
+
+       g_free(uri);
+
+       gtk_widget_hide(GTK_WIDGET(dialog));
+       gtk_object_destroy(GTK_OBJECT(dialog));
+}
+
+static void open_pressed(GtkWidget *button, gpointer user_data)
+{
+       OctopusPlayer *player=(OctopusPlayer *)user_data;
+       GtkWidget     *dialog;
+
+       dialog = gtk_file_chooser_dialog_new("Select file", player->priv->toplevel, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+       g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(open_response), player);
+       gtk_widget_show(dialog);
+}
+
+static void play_pressed(GtkWidget *button, gpointer user_data)
+{
+       OctopusPlayer *player=(OctopusPlayer *)user_data;
+
+       com_ixs_OctopusService_play(player->priv->octopus_proxy, player->priv->route_id, NULL);
+}
+
+static void pause_pressed(GtkWidget *button, gpointer user_data)
+{
+       OctopusPlayer *player=(OctopusPlayer *)user_data;
+
+       com_ixs_OctopusService_pause(player->priv->octopus_proxy, player->priv->route_id, NULL);
+}
+
+static void stop_pressed(GtkWidget *button, gpointer user_data)
+{
+       OctopusPlayer *player=(OctopusPlayer *)user_data;
+
+       com_ixs_OctopusService_stop(player->priv->octopus_proxy, player->priv->route_id, NULL);
+}
+
+static void quit_pressed(GtkWidget *button, gpointer user_data)
+{
+       gtk_main_quit();
+}
+
+/* Octopus event handlers */
+
+static void playback_error(DBusGProxy *proxy, guint id, gchar *error, gpointer user_data)
+{
+       OctopusPlayer *player=(OctopusPlayer *)user_data;
+
+       if(id == player->priv->route_id) {
+               GtkWidget *dlg;
+
+               dlg = gtk_message_dialog_new(player->priv->toplevel, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", error);
+               gtk_widget_show(dlg);
+               g_signal_connect_swapped(dlg, "response", G_CALLBACK(gtk_object_destroy), dlg);
+       }
+}
+
+static void stream_position(DBusGProxy *proxy, guint id, guint pos, guint dur, gpointer user_data)
+{
+       OctopusPlayer *player=(OctopusPlayer *)user_data;
+
+       if(id == player->priv->route_id) {
+               gchar *text;
+
+               text = g_strdup_printf("%d:%02d / %d:%02d", pos/60, pos%60, dur/60, dur%60);
+               gtk_progress_bar_set_text(GTK_PROGRESS_BAR(player->priv->progress), text);
+               if(dur)
+                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(player->priv->progress), (double)pos/dur);
+               else
+                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(player->priv->progress), 0.0);
+               g_free(text);
+       }
+}
diff --git a/player/octopus-player.h b/player/octopus-player.h
new file mode 100644 (file)
index 0000000..daf1a89
--- /dev/null
@@ -0,0 +1,46 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef OCTOPUS_PLAYER_H_
+#define OCTOPUS_PLAYER_H_
+
+#include <glib-object.h>
+
+#define OCTOPUS_PLAYER_TYPE (octopus_player_get_type())
+#define OCTOPUS_PLAYER(inst) (G_TYPE_CHECK_INSTANCE_CAST((inst), OCTOPUS_PLAYER_TYPE, OctopusPlayer))
+
+typedef struct _OctopusPlayerClass   OctopusPlayerClass;
+typedef struct _OctopusPlayer        OctopusPlayer;
+typedef struct _OctopusPlayerPrivate OctopusPlayerPrivate;
+
+struct _OctopusPlayerClass
+{
+       GObjectClass parent;
+};
+
+struct _OctopusPlayer
+{
+       GObject parent;
+
+       OctopusPlayerPrivate *priv;
+};
+
+GType octopus_player_get_type(void);
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..eb33a3e
--- /dev/null
@@ -0,0 +1,42 @@
+bin_PROGRAMS = octopus
+
+octopus_SOURCES =      main.c                          \
+                       octopus-server.c                \
+                       octopus-route.c         \
+                       octopus-backend.c               \
+                       octopus-backend-gst.c   \
+                       octopus-module.c                \
+                       octopus-module-manager.c \
+                       octopus-marshals.c
+
+noinst_HEADERS =       octopus-server.h                \
+                       octopus-route.h         \
+                       octopus-backend-gst.h
+
+
+octopusincludedir =    $(includedir)/octopus
+
+octopusinclude_HEADERS = \
+                       octopus-backend.h               \
+                       octopus-module.h        \
+                       octopus-module-manager.h
+
+AM_CFLAGS = $(glib_CFLAGS) $(dbus_CFLAGS) $(dbus_glib_CFLAGS) $(gmodule_CFLAGS) $(gnome_vfs_CFLAGS) $(gstreamer_CFLAGS) $(x11_CFLAGS) -DPLUGINDIR=\"${MODULEDIR}\" -DCOMPONENTDIR=\"${COMPONENTDIR}\"
+
+octopus_LDADD = $(glib_LIBS) $(dbus_LIBS) $(dbus_glib_LIBS) $(gmodule_LIBS) $(gnome_vfs_LIBS) $(gstreamer_LIBS) $(x11_LIBS) -lgstinterfaces-0.10
+
+BUILT_SOURCES = octopus-glue.h \
+                                                               octopus-marshals.h \
+                                                               octopus-marshals.c
+
+octopus-glue.h: octopus-dbus.xml
+       dbus-binding-tool --prefix=octopus_server --mode=glib-server --output=$@ $<
+
+octopus-marshals.h: octopus-marshals.spec
+       glib-genmarshal --header $< >$@
+
+octopus-marshals.c: octopus-marshals.spec
+       echo '#include "octopus-marshals.h"' >$@
+       glib-genmarshal --body $< >>$@
+
+CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..610f314
--- /dev/null
@@ -0,0 +1,64 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <stdlib.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "octopus-server.h"
+
+int
+main (int argc, char *argv[])
+{
+    GMainLoop       *loop;
+    OctopusServer   *server = NULL;
+
+    g_type_init ();
+    gst_init (&argc, &argv);
+
+    loop = g_main_loop_new (NULL, FALSE);
+
+    /* Initialize Octopus Server */
+    server = OCTOPUS_SERVER (
+        g_object_new (OCTOPUS_TYPE_SERVER, NULL)
+    );
+
+    if (!octopus_server_register_service (server)) {
+        g_object_unref (server);
+
+        g_error ("Registering Octopus Service failed.\n");
+    }
+
+    g_main_loop_run (loop);
+
+       return 0;
+}
+
+/* Emacs indentatation information
+   Local Variables:
+   indent-tabs-mode:nil
+   tab-width:4
+   c-set-offset:4
+   c-basic-offset:4
+   End:
+*/
+// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
+
diff --git a/src/octopus-backend-gst.c b/src/octopus-backend-gst.c
new file mode 100644 (file)
index 0000000..4558014
--- /dev/null
@@ -0,0 +1,793 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <string.h>
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+#include "octopus-backend-gst.h"
+#include "octopus-module.h"
+#include "octopus-module-manager.h"
+#include "octopus-route.h"
+
+typedef struct _RouteDataGst RouteDataGst;
+
+struct _RouteDataGst
+{
+  GstElement *pipeline;
+  GstElement *fakesink;
+  gboolean   ready;
+  guint      timeout_tag;
+};
+
+static gboolean
+validate_component(OctopusBackend   *backend,
+                   OctopusComponent *comp);
+static gboolean
+can_link_components(OctopusBackend *backend,
+                    OctopusComponent *comp1,
+                    OctopusComponent *comp2);
+
+gboolean
+init_route (OctopusBackend *backend,
+            OctopusRoute   *route);
+static void
+destroy_route(OctopusBackend *backend,
+              OctopusRoute   *route);
+static gboolean
+play_route(OctopusBackend *backend,
+           OctopusRoute   *route);
+static gboolean
+pause_route(OctopusBackend *backend,
+            OctopusRoute   *route);
+static gboolean
+stop_route(OctopusBackend *backend,
+           OctopusRoute   *route);
+
+static void
+route_mapping_changed(OctopusRoute *route,
+                      gpointer user_data);
+static void
+route_endpoint_changed(OctopusRoute    *route,
+                       OctopusEndpoint *endpoint,
+                       gpointer        user_data);
+static void
+have_type(GstTypeFind *typefind,
+          guint       probability,
+          GstCaps     *caps,
+          gpointer    user_data);
+static void
+pad_added(GstElement *elem,
+          GstPad     *pad,
+          gpointer   *user_data);
+static void
+pad_removed(GstElement *elem,
+            GstPad     *pad,
+            gpointer   *user_data);
+static void
+no_more_pads(GstElement *elem,
+             gpointer   user_data);
+static gboolean
+bus_watch(GstBus     *bus,
+          GstMessage *msg,
+          gpointer   user_data);
+static gboolean
+position_timer(gpointer);
+
+G_DEFINE_TYPE(OctopusBackendGst, octopus_backend_gst, OCTOPUS_TYPE_BACKEND)
+
+static void
+octopus_backend_gst_class_init(OctopusBackendGstClass *klass)
+{
+  OctopusBackendClass *backend;
+
+  backend = OCTOPUS_BACKEND_CLASS(klass);
+
+  backend->validate_component = validate_component;
+  backend->can_link_components = can_link_components;
+  backend->init_route = init_route;
+  backend->destroy_route = destroy_route;
+  backend->play_route = play_route;
+  backend->pause_route = pause_route;
+  backend->stop_route = stop_route;
+}
+
+static void
+octopus_backend_gst_init(OctopusBackendGst *backend)
+{
+  OctopusBackend *base;
+
+  base = OCTOPUS_BACKEND(backend);
+
+  base->name = "gstreamer";
+}
+
+/*
+ * Helper functions
+ */
+
+static gboolean
+can_maybe_sink_caps(GstElementFactory *factory,
+                    GstCaps *caps)
+{
+  const GList *pads;
+  gboolean    result = FALSE;
+
+  pads = gst_element_factory_get_static_pad_templates(factory);
+  for(; pads; pads = pads->next) {
+    GstStaticPadTemplate *tmpl = pads->data;
+    if(tmpl->direction == GST_PAD_SINK && tmpl->presence == GST_PAD_ALWAYS) {
+      caps = gst_caps_intersect(caps, gst_static_pad_template_get_caps(tmpl));
+      result = !gst_caps_is_empty(caps);
+      gst_caps_unref(caps);
+      break;
+    }
+  }
+
+  return result;
+}
+
+/*
+ * OctopusBackend implementation
+ */
+
+static gboolean
+validate_component(OctopusBackend   *backend,
+                   OctopusComponent *comp)
+{
+  GSList            *iter;
+
+  for(iter = comp->elements; iter; iter = iter->next) {
+    GstElementFactory *factory;
+    
+    factory = gst_element_factory_find(((OctopusElement *)iter->data)->name);
+    if(!factory)
+      return FALSE;
+    gst_object_unref(factory);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+can_link_components(OctopusBackend   *backend,
+                    OctopusComponent *comp1,
+                    OctopusComponent *comp2)
+{
+  OctopusElement    *elem;
+  GstElementFactory *factory;
+  const GList       *pads;
+  GstCaps           *caps = NULL;
+  gboolean          result = FALSE;
+
+  elem = (OctopusElement *)g_slist_last(comp1->elements)->data;
+  factory = gst_element_factory_find(elem->name);
+
+  pads = gst_element_factory_get_static_pad_templates(factory);
+  for(; pads; pads = pads->next) {
+    GstStaticPadTemplate *tmpl = pads->data;
+    if(tmpl->direction == GST_PAD_SRC && tmpl->presence == GST_PAD_ALWAYS) {
+      caps = gst_static_pad_template_get_caps(tmpl);
+      break;
+    }
+  }
+  gst_object_unref(factory);
+
+  if(caps) {
+    if(gst_caps_is_any(caps))
+      return FALSE;
+
+    elem = (OctopusElement *)comp2->elements->data;
+    factory = gst_element_factory_find(elem->name);
+    result = can_maybe_sink_caps(factory, caps);
+    gst_object_unref(factory);
+  }
+
+  return result;
+}
+
+gboolean
+init_route(OctopusBackend *backend,
+           OctopusRoute   *route)
+{
+  RouteDataGst *data;
+  GstBus       *bus;
+
+  data = g_new0(RouteDataGst, 1);
+  data->pipeline = gst_element_factory_make("pipeline", NULL);
+  g_object_set_data(G_OBJECT(route), "data", data);
+
+  g_signal_connect(route, "mapping-changed", G_CALLBACK(route_mapping_changed), 0);
+  g_signal_connect(route, "endpoint-changed", G_CALLBACK(route_endpoint_changed), 0);
+
+  bus = gst_pipeline_get_bus(GST_PIPELINE(data->pipeline));
+  gst_bus_add_watch(bus, bus_watch, route);
+
+  return TRUE;
+}
+
+static void
+destroy_route(OctopusBackend *backend,
+              OctopusRoute   *route)
+{
+  RouteDataGst *data;
+
+  stop_route(backend, route);
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+  gst_object_unref(GST_OBJECT(data->pipeline));
+}
+
+static gboolean
+play_route(OctopusBackend *backend,
+           OctopusRoute   *route)
+{
+  RouteDataGst         *data;
+  GstStateChangeReturn ret;
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+  if(!data->ready && !data->fakesink) {
+    /* Keep the pipeline in an async state change while building it */
+    data->fakesink = gst_element_factory_make("fakesink", NULL);
+    gst_bin_add(GST_BIN(data->pipeline), data->fakesink);
+  }
+
+  ret = gst_element_set_state(GST_ELEMENT(data->pipeline), GST_STATE_PLAYING);
+  if(ret == GST_STATE_CHANGE_FAILURE) {
+    g_debug("Pipeline failed to start playback");
+    return FALSE;
+  } else if(ret != GST_STATE_CHANGE_ASYNC) {
+    octopus_route_state_changed(route, OCTOPUS_ROUTE_PLAYING);
+  }
+
+  if(!data->timeout_tag)
+    data->timeout_tag = g_timeout_add(1000, position_timer, route);
+
+  return TRUE;
+}
+
+static gboolean
+pause_route(OctopusBackend *backend,
+            OctopusRoute   *route)
+{
+  RouteDataGst         *data;
+  GstStateChangeReturn ret;
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+
+  if(data->timeout_tag) {
+    g_source_remove(data->timeout_tag);
+    data->timeout_tag = 0;
+  }
+
+  ret = gst_element_set_state(GST_ELEMENT(data->pipeline), GST_STATE_PAUSED);
+  if(ret == GST_STATE_CHANGE_FAILURE) {
+    g_debug("Pipeline failed to pause playback");
+    return FALSE;
+  } else if(ret != GST_STATE_CHANGE_ASYNC) {
+    octopus_route_state_changed(route, OCTOPUS_ROUTE_PAUSED);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+stop_route(OctopusBackend *backend,
+           OctopusRoute   *route)
+{
+  RouteDataGst         *data;
+  GstStateChangeReturn ret;
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+
+  if(data->timeout_tag) {
+    g_source_remove(data->timeout_tag);
+    data->timeout_tag = 0;
+  }
+
+  ret = gst_element_set_state(GST_ELEMENT(data->pipeline), GST_STATE_NULL);
+  if(ret == GST_STATE_CHANGE_FAILURE) {
+    g_debug("Pipeline failed to stop playback");
+    return FALSE;
+  } else if(ret != GST_STATE_CHANGE_ASYNC) {
+    octopus_route_state_changed(route, OCTOPUS_ROUTE_STOPPED);
+  }
+
+  return TRUE;
+}
+
+/*
+ * Private functions
+ */
+
+static void
+configure_endpoint(GstElement            *elem,
+                   const OctopusEndpoint *endpoint)
+{
+  if(endpoint->uri) {
+    g_debug("Setting URI '%s'", endpoint->uri);
+    g_object_set(G_OBJECT(elem), "location", endpoint->uri, NULL);
+  }
+
+  if(endpoint->window) {
+    g_debug("Setting X11 display ('%s', %lx)", endpoint->display, endpoint->window);
+    g_object_set(G_OBJECT(elem), "display", endpoint->display, NULL);
+    gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(elem), endpoint->window);
+  }
+}
+
+static GstElement *
+realize_element(OctopusRoute   *route,
+                OctopusElement *oct_elem)
+{
+  GstElement *gst_elem;
+  GList      *iter;
+  GSList     *iter2;
+
+  g_debug("Realizing element '%s'", oct_elem->name);
+  gst_elem = gst_element_factory_make(oct_elem->name, oct_elem->endpoint);
+
+  if(oct_elem->endpoint) {
+    const OctopusEndpoint *endpoint;
+    if((endpoint = octopus_route_get_endpoint(route, oct_elem->endpoint)))
+      configure_endpoint(gst_elem, endpoint);
+  }
+
+  for(iter2 = oct_elem->parameters; iter2; iter2 = iter2->next) {
+    OctopusParameter *param = (OctopusParameter *)iter2->data;
+    GParamSpec       *pspec;
+
+    pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(gst_elem), param->name);
+    if(pspec) {
+      if(pspec->value_type == G_TYPE_INT) {
+        g_object_set(G_OBJECT(gst_elem), param->name, strtol(param->value, NULL, 0), NULL);
+      } else if(pspec->value_type == G_TYPE_UINT) {
+        g_object_set(G_OBJECT(gst_elem), param->name, strtoul(param->value, NULL, 0), NULL);
+      } else if(pspec->value_type == G_TYPE_INT64) {
+        g_object_set(G_OBJECT(gst_elem), param->name, strtoll(param->value, NULL, 0), NULL);
+      } else if(pspec->value_type == G_TYPE_UINT64) {
+        g_object_set(G_OBJECT(gst_elem), param->name, strtoull(param->value, NULL, 0), NULL);
+      } else if(pspec->value_type == G_TYPE_BOOLEAN) {
+        if(!strcmp(param->value, "true"))
+          g_object_set(G_OBJECT(gst_elem), param->name, TRUE, NULL);
+        else if(!strcmp(param->value, "false"))
+          g_object_set(G_OBJECT(gst_elem), param->name, FALSE, NULL);
+      } else if(pspec->value_type == G_TYPE_STRING) {
+        g_object_set(G_OBJECT(gst_elem), param->name, param->value, NULL);
+      }
+    } else {
+      g_debug("Unknown parameter '%s' specified for element '%s'", param->name, oct_elem->name);
+    }
+  }
+
+  /* XXX better way to figure out if it's a typefind? */
+  if(g_str_has_prefix(oct_elem->name, "typefind")) {
+    g_debug("Connecting to 'have-type' signal");
+    g_signal_connect(G_OBJECT(gst_elem), "have-type", G_CALLBACK(have_type), route);
+  }
+
+  iter = gst_element_class_get_pad_template_list(GST_ELEMENT_GET_CLASS(gst_elem));
+  for(; iter; iter = iter->next) {
+    GstPadTemplate *tmpl = (GstPadTemplate *)iter->data;
+    if(GST_PAD_TEMPLATE_DIRECTION(tmpl) == GST_PAD_SRC
+       && GST_PAD_TEMPLATE_PRESENCE(tmpl) == GST_PAD_SOMETIMES)
+    {
+      g_debug("Connecting to dynamic pad signals");
+      g_signal_connect(G_OBJECT(gst_elem), "pad-added", G_CALLBACK(pad_added), route);
+      g_signal_connect(G_OBJECT(gst_elem), "pad-removed", G_CALLBACK(pad_removed), route);
+      g_signal_connect(G_OBJECT(gst_elem), "no-more-pads", G_CALLBACK(no_more_pads), route);
+      break;
+    }
+  }
+
+  return gst_elem;
+}
+
+static GstPad *
+realize_component(OctopusBackend   *backend,
+                  OctopusRoute     *route,
+                  OctopusComponent *component,
+                  GstPad           *srcpad)
+{
+  RouteDataGst *data;
+  GSList       *iter;
+  GstState     state;
+
+  g_debug("Realizing component '%s'", component->name);
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+
+  state = GST_STATE(data->pipeline);
+  if(state != GST_STATE_NULL)
+    state = GST_STATE_PAUSED;
+
+  for(iter = component->elements; iter; iter = iter->next) {
+    OctopusElement *oct_elem = (OctopusElement *)iter->data;
+    GstElement     *gst_elem;
+
+    gst_elem = realize_element(route, oct_elem);
+    gst_bin_add(GST_BIN(data->pipeline), gst_elem);
+
+    if(srcpad) {
+      GstPad *sinkpad;
+
+      if((sinkpad = gst_element_get_static_pad(gst_elem, "sink"))) {
+        if(GST_PAD_LINK_FAILED(gst_pad_link(srcpad, sinkpad)))
+          g_debug("Linking pads failed - component '%s' may be broken", component->name);
+        gst_object_unref(sinkpad);
+      }
+
+      gst_object_unref(srcpad);
+    }
+    gst_element_set_state(gst_elem, state);
+
+    srcpad = gst_element_get_static_pad(gst_elem, "src");
+  }
+
+  return srcpad;
+}
+
+static void
+realize_component_chain(OctopusBackend *backend,
+                        OctopusRoute   *route,
+                        GSList         *chain,
+                        GstPad         *srcpad)
+{
+  GSList *iter;
+
+  if(srcpad)
+    gst_object_ref(srcpad);
+  for(iter = chain; iter; iter = iter->next)
+    srcpad = realize_component(backend, route, (OctopusComponent *)iter->data, srcpad);
+  if(srcpad)
+    gst_object_unref(srcpad);
+}
+
+static void
+continue_route_from_pad(OctopusBackend *backend,
+                        OctopusRoute   *route,
+                        GstPad         *srcpad)
+{
+  GstCaps *caps;
+  GSList  *iter;
+  gchar   **sinks;
+  GSList  *chain = NULL;
+
+  caps = gst_pad_get_caps(srcpad);
+  g_object_get(G_OBJECT(route), "sinks", &sinks, NULL);
+
+  for(iter = backend->components; (!chain && iter); iter = iter->next) {
+    OctopusComponent  *comp = (OctopusComponent *)iter->data;
+    OctopusElement    *elem = (OctopusElement *)comp->elements->data;
+    GstElementFactory *factory;
+
+    factory = gst_element_factory_find(elem->name);
+    if(can_maybe_sink_caps(factory, caps)) {
+      unsigned j;
+
+      g_debug("%s[%s] can sink this", comp->name, elem->name);
+
+      for(j = 0; (!chain && sinks[j]); ++j)
+        chain = octopus_backend_build_component_chain(backend, comp, sinks[j]);
+    }
+    gst_object_unref(factory);
+  }
+  gst_caps_unref(caps);
+
+  if(chain) {
+    realize_component_chain(backend, route, chain, srcpad);
+    g_slist_free(chain);
+  } else {
+    g_debug("Unable to continue route");
+    octopus_route_playback_error(route, "Unable to build route: unsupported media type");
+  }
+}
+
+static void
+route_mapping_changed(OctopusRoute *route,
+                      gpointer user_data)
+{
+  OctopusBackend   *backend;
+  OctopusComponent *component;
+  gchar            **sources;
+  gchar            **sinks;
+  unsigned         i;
+
+  g_debug("Route mapping changed");
+
+  g_object_get(G_OBJECT(route), "backend", &backend,
+                                "sources", &sources,
+                                "sinks", &sinks, NULL);
+  g_assert(OCTOPUS_IS_BACKEND_GST(backend));
+
+  for(i = 0; sources[i]; ++i) {
+    GSList   *chain = NULL;
+    unsigned j;
+
+    component = octopus_backend_get_component_by_endpoint(backend, sources[i], OCTOPUS_ENDPOINT_SOURCE);
+    if(!component)
+      continue;
+
+    for(j = 0; sinks[j]; ++j) {
+      if((chain = octopus_backend_build_component_chain(backend, component, sinks[j]))) {
+        realize_component_chain(backend, route, chain, NULL);
+        g_slist_free(chain);
+        break;
+      }
+    }
+  }
+
+  /* TODO */
+}
+
+static void
+route_endpoint_changed(OctopusRoute    *route,
+                       OctopusEndpoint *endpoint,
+                       gpointer        user_data)
+{
+  RouteDataGst *data;
+  GstIterator  *iter;
+  gpointer     element;
+  gboolean     found = FALSE;
+
+  g_debug("Route endpoint '%s' changed", endpoint->name);
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+
+  iter = gst_bin_iterate_recurse(GST_BIN(data->pipeline));
+  while(!found && gst_iterator_next(iter, &element) == GST_ITERATOR_OK) {
+    gchar *elem_name;
+
+    g_object_get(G_OBJECT(element), "name", &elem_name, NULL);
+    if(g_str_has_prefix(elem_name, endpoint->name)) {
+      configure_endpoint(element, endpoint);
+      found = TRUE;
+    }
+
+    g_free(elem_name);
+    gst_object_unref(element);
+  }
+  gst_iterator_free(iter);
+}
+
+static void
+have_type(GstTypeFind *typefind,
+          guint       probability,
+          GstCaps     *caps,
+          gpointer    user_data)
+{
+  OctopusRoute   *route;
+  OctopusBackend *backend;
+  GstPad         *pad;
+  gchar          *caps_str;
+
+  route = OCTOPUS_ROUTE(user_data);
+  g_object_get(G_OBJECT(route), "backend", &backend, NULL);
+  g_assert(OCTOPUS_IS_BACKEND_GST(backend));
+
+  caps_str = gst_caps_to_string(caps);
+  g_debug("Have type: %s", caps_str);
+  g_free(caps_str);
+
+  pad = gst_element_get_static_pad(GST_ELEMENT(typefind), "src");
+  continue_route_from_pad(backend, route, pad);
+  g_object_unref(pad);
+}
+
+static void
+pad_added(GstElement *elem,
+          GstPad     *pad,
+          gpointer   *user_data)
+{
+  OctopusRoute   *route;
+  OctopusBackend *backend;
+  RouteDataGst   *data;
+  gchar          *elem_name;
+  gchar          *pad_name;
+  GstCaps        *caps;
+  gchar          *caps_str;
+  GstElement     *queue;
+  GstPad         *qpad;
+
+  g_object_get(G_OBJECT(elem), "name", &elem_name, NULL);
+  g_object_get(G_OBJECT(pad), "name", &pad_name, NULL);
+  caps = gst_pad_get_caps(pad);
+  caps_str = gst_caps_to_string(caps);
+  g_debug("Pad '%s' added to '%s', caps: %s", pad_name, elem_name, caps_str);
+  gst_caps_unref(caps);
+  g_free(caps_str);
+  g_free(elem_name);
+  g_free(pad_name);
+
+  route = OCTOPUS_ROUTE(user_data);
+  g_object_get(G_OBJECT(route), "backend", &backend, NULL);
+  g_assert(OCTOPUS_IS_BACKEND_GST(backend));
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+
+  queue = gst_element_factory_make("queue", NULL);
+  gst_bin_add(GST_BIN(data->pipeline), queue);
+  gst_element_set_state(queue, GST_STATE_PAUSED);
+  qpad = gst_element_get_static_pad(queue, "sink");
+  gst_pad_link(pad, qpad);
+  gst_object_unref(qpad);
+
+  qpad = gst_element_get_static_pad(queue, "src");
+  continue_route_from_pad(backend, route, qpad);
+  gst_object_unref(qpad);
+}
+
+static void
+pad_removed(GstElement *elem,
+            GstPad     *pad,
+            gpointer   *user_data)
+{
+  OctopusRoute *route;
+  RouteDataGst *data;
+  gchar        *elem_name;
+  gchar        *pad_name;
+
+  if(gst_pad_get_direction(pad) != GST_PAD_SRC)
+    return;
+
+  g_object_get(G_OBJECT(elem), "name", &elem_name, NULL);
+  g_object_get(G_OBJECT(pad), "name", &pad_name, NULL);
+  g_debug("Pad '%s' removed from '%s'", pad_name, elem_name);
+  g_free(elem_name);
+  g_free(pad_name);
+
+  route = OCTOPUS_ROUTE(user_data);
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+  data->ready = FALSE;
+
+  while(1) {
+    GstIterator *iter;
+    gpointer    elem;
+    GSList      *stale = NULL;
+    GSList      *ptr;
+
+    /* Find any elements that have a sink pad with nothing connected */
+    iter = gst_bin_iterate_elements(GST_BIN(data->pipeline));
+    while(gst_iterator_next(iter, &elem) != GST_ITERATOR_DONE) {
+      GstPad *pad = gst_element_get_static_pad(GST_ELEMENT(elem), "sink");
+      if(pad) {
+        GstPad *peer = gst_pad_get_peer(pad);
+        if(peer)
+          gst_object_unref(peer);
+        else
+          stale = g_slist_prepend(stale, elem);
+        gst_object_unref(pad);
+      }
+      gst_object_unref(GST_OBJECT(elem));
+    }
+    gst_iterator_free(iter);
+
+    /* No stale elements found - we're done */
+    if(!stale)
+      break;
+
+    for(ptr = stale; ptr; ptr = ptr->next) {
+      GstElement *elem;
+      gchar      *name;
+
+      elem = GST_ELEMENT(ptr->data);
+      g_object_get(G_OBJECT(elem), "name", &name, NULL);
+      g_debug("Removing element '%s'", name);
+      gst_element_set_state(elem, GST_STATE_NULL);
+      gst_bin_remove(GST_BIN(data->pipeline), elem);
+    }
+  }
+}
+
+static void
+no_more_pads(GstElement *elem,
+             gpointer   user_data)
+{
+  OctopusRoute *route;
+  RouteDataGst *data;
+  gchar        *elem_name;
+
+  g_object_get(G_OBJECT(elem), "name", &elem_name, NULL);
+  g_debug("No more pads from '%s'", elem_name);
+  g_free(elem_name);
+
+  route = OCTOPUS_ROUTE(user_data);
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+  if(data->fakesink) {
+    gst_element_set_state(data->fakesink, GST_STATE_NULL);
+    gst_bin_remove(GST_BIN(data->pipeline), data->fakesink);
+    data->fakesink = NULL;
+    data->ready = TRUE;
+  }
+}
+
+static gboolean
+bus_watch(GstBus     *bus,
+          GstMessage *msg,
+          gpointer   user_data)
+{
+  OctopusRoute   *route;
+  RouteDataGst   *data;
+  OctopusBackend *backend;
+  
+  route = OCTOPUS_ROUTE(user_data);
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+  g_object_get(G_OBJECT(route), "backend", &backend, NULL);
+  
+  switch(GST_MESSAGE_TYPE(msg)) {
+  case GST_MESSAGE_ERROR: {
+    GError *error = NULL;
+
+    gst_message_parse_error(msg, &error, NULL);
+    g_debug("GST ERROR: %s", error->message);
+    octopus_route_playback_error(route, error->message);
+    g_error_free(error);
+    stop_route(backend, route);
+    break; }
+
+  case GST_MESSAGE_EOS:
+    g_debug("GST EOS");
+    stop_route(backend, route);
+    break;
+
+  case GST_MESSAGE_STATE_CHANGED: {
+    GstElement *elem;
+    gchar      *name;
+
+    GstState previous, state, pending;
+    gst_message_parse_state_changed (msg, &previous, &state, &pending);
+    elem = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+    name = gst_element_get_name(elem);
+    g_debug("GST STATE CHANGED: %s %s->%s, pending %s", name, gst_element_state_get_name(previous), gst_element_state_get_name(state), gst_element_state_get_name(pending));
+    g_free(name);
+
+    if(elem == data->pipeline && pending == GST_STATE_VOID_PENDING) {
+      if(state == GST_STATE_NULL)
+        octopus_route_state_changed(route, OCTOPUS_ROUTE_STOPPED);
+      else if(state == GST_STATE_PAUSED)
+        octopus_route_state_changed(route, OCTOPUS_ROUTE_PAUSED);
+      else if(state == GST_STATE_PLAYING)
+        octopus_route_state_changed(route, OCTOPUS_ROUTE_PLAYING);
+    }
+    break; }
+
+  default:;
+    g_debug("Unhandled GST message of type %d", GST_MESSAGE_TYPE(msg));
+  }
+
+  return TRUE;
+}
+
+static gboolean
+position_timer(gpointer user_data)
+{
+  OctopusRoute *route = (OctopusRoute *)user_data;
+  RouteDataGst *data;
+  GstFormat    fmt = GST_FORMAT_TIME;
+  gint64       pos = 0;
+  gint64       dur = 0;
+
+  data = (RouteDataGst *)g_object_get_data(G_OBJECT(route), "data");
+  gst_element_query_position(data->pipeline, &fmt, &pos);
+  gst_element_query_duration(data->pipeline, &fmt, &dur);
+
+  octopus_route_stream_position(route, (guint)(pos/1000000000), (guint)(dur/1000000000));
+
+  return TRUE;
+}
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-backend-gst.h b/src/octopus-backend-gst.h
new file mode 100644 (file)
index 0000000..b7213a7
--- /dev/null
@@ -0,0 +1,58 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef _OCTOPUS_BACKEND_GST_H_
+#define _OCTOPUS_BACKEND_GST_H_
+
+#include "octopus-backend.h"
+
+#define OCTOPUS_TYPE_BACKEND_GST             \
+        (octopus_backend_gst_get_type())
+#define OCTOPUS_BACKEND_GST(obj)             \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), OCTOPUS_TYPE_BACKEND_GST, OctopusBackendGst))
+#define OCTOPUS_IS_BACKEND_GST(obj)          \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), OCTOPUS_TYPE_BACKEND_GST))
+#define OCTOPUS_BACKEND_GST_CLASS(klass)     \
+        (G_TYPE_CHECK_CLASS_CAST((klass), OCTOPUS_TYPE_BACKEND_GST, OctopusBackendGstClass))
+#define OCTOPUS_IS_BACKEND_GST_CLASS(klass)  \
+        (G_TYPE_CHECK_CLASS_TYPE((klass), OCTOPUS_TYPE_BACKEND_GST))
+#define OCTOPUS_BACKEND_GST_GET_CLASS(obj)   \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), OCTOPUS_TYPE_BACKEND_GST, OctopusBackendGstClass))
+
+typedef struct _OctopusBackendGst        OctopusBackendGst;
+typedef struct _OctopusBackendGstClass   OctopusBackendGstClass;
+typedef struct _OctopusBackendGstPrivate OctopusBackendGstPrivate;
+
+struct _OctopusBackendGst
+{
+  OctopusBackend           parent;
+  OctopusBackendGstPrivate *priv;
+};
+
+struct _OctopusBackendGstClass
+{
+  OctopusBackendClass parent;
+};
+
+GType
+octopus_backend_gst_get_type(void);
+
+#endif
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-backend.c b/src/octopus-backend.c
new file mode 100644 (file)
index 0000000..c88adf6
--- /dev/null
@@ -0,0 +1,398 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include "octopus-backend.h"
+#include "octopus-module-manager.h"
+
+enum
+{
+  PROP_NAME = 1,
+  PROP_MODULE_MANAGER
+};
+
+static void
+set_property(GObject      *object,
+             guint        prop_id,
+             const GValue *value,
+             GParamSpec   *pspec);
+static void
+get_property(GObject    *object,
+             guint      prop_id,
+             GValue     *value,
+             GParamSpec *pspec);
+
+G_DEFINE_ABSTRACT_TYPE(OctopusBackend, octopus_backend, G_TYPE_OBJECT)
+
+static void
+octopus_backend_class_init(OctopusBackendClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS(klass);
+
+  gobject_class->set_property = set_property;
+  gobject_class->get_property = get_property;
+
+  g_object_class_install_property(gobject_class, PROP_NAME,
+    g_param_spec_string("name", "Name", "Name of the backend", NULL,
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property(gobject_class, PROP_MODULE_MANAGER,
+    g_param_spec_pointer("module-manager", "Module manager", "",
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+octopus_backend_init(OctopusBackend *backend)
+{
+  backend->name = NULL;
+  backend->module_manager = NULL;
+  backend->components = NULL;
+}
+
+static void
+set_property(GObject      *object,
+             guint        prop_id,
+             const GValue *value,
+             GParamSpec   *pspec)
+{
+  OctopusBackend *backend;
+
+  g_assert(OCTOPUS_IS_BACKEND(object));
+  backend = (OctopusBackend *)object;
+
+  switch(prop_id)
+  {
+  case PROP_MODULE_MANAGER:
+    backend->module_manager = OCTOPUS_MODULE_MANAGER(g_value_get_pointer(value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+  };
+}
+
+static void
+get_property(GObject    *object,
+             guint      prop_id,
+             GValue     *value,
+             GParamSpec *pspec)
+{
+  OctopusBackend *backend;
+
+  g_assert(OCTOPUS_IS_BACKEND(object));
+  backend = (OctopusBackend *)object;
+
+  switch(prop_id)
+  {
+  case PROP_NAME:
+    g_value_set_string(value, backend->name);
+    break;
+  case PROP_MODULE_MANAGER:
+    g_value_set_pointer(value, backend->module_manager);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+  };
+}
+
+/*
+ * Helper functions
+ */
+
+static gboolean
+component_priority_compare(const OctopusComponent *comp1,
+                           const OctopusComponent *comp2)
+{
+  if(comp1->priority > comp2->priority)
+    return -1;
+  else if(comp1->priority < comp2->priority)
+    return 1;
+  else
+    return 0;
+}
+
+/*
+ * Internal functions
+ */
+
+static GSList *
+build_component_chain_recursive(OctopusBackend   *backend,
+                                OctopusComponent *source,
+                                const gchar      *sink)
+{
+  OctopusElement *elem;
+  GSList         *result = NULL;
+  GSList         *iter;
+
+  elem = (OctopusElement *)g_slist_last(source->elements)->data;
+  if(source->dynamic || (elem->ep_type == OCTOPUS_ENDPOINT_SINK
+                         && g_str_has_prefix(elem->endpoint, sink)))
+  {
+    result = g_slist_prepend(result, source);
+    return result;
+  }
+
+  for(iter = source->links; iter; iter = iter->next) {
+    OctopusComponent *comp = (OctopusComponent *)iter->data;
+    if((result = build_component_chain_recursive(backend, comp, sink))) {
+      result = g_slist_prepend(result, source);
+      return result;
+    }
+  }
+
+  return NULL;
+}
+
+/*
+ * Public API
+ */
+
+void
+octopus_backend_load_components(OctopusBackend *backend,
+                                gchar          *filename)
+{
+  FILE                *file;
+  OctopusComponent    *component = NULL;
+  OctopusElement      *element = NULL;
+  GSList              *iter1;
+  GSList              *iter2;
+  OctopusBackendClass *klass;
+  GSList              *new_comps = NULL;
+
+  file = fopen(filename, "r");
+  if(!file)
+    return;
+
+  g_debug("Loading components from '%s'", filename);
+
+  /* XXX Crappy parser, replace with a better format or at least plug all the holes */
+  while(1) {
+    char line[1024];
+    char *ptr;
+    gchar **parts;
+
+    if(!fgets(line, sizeof(line), file))
+      break;
+
+    for(ptr = line; isspace(*ptr); ++ptr) ;
+    if(*ptr==0 || *ptr=='#')
+      continue;
+
+    parts = g_strsplit_set(ptr, " \t\n", -1);
+    if(!strcmp(parts[0], "component")) {
+      component = g_new0(OctopusComponent, 1);
+      component->name = g_strdup(parts[1]);
+      new_comps = g_slist_prepend(new_comps, component);
+      element = NULL;
+    } else if(component && !strcmp(parts[0], "element")) {
+      element = g_new0(OctopusElement, 1);
+      element->name = g_strdup(parts[1]);
+      component->elements = g_slist_append(component->elements, element);
+    } else if(component && !strcmp(parts[0], "module")) {
+      element = g_new0(OctopusElement, 1);
+      element->name = g_strconcat("?", parts[1], NULL);
+      component->elements = g_slist_append(component->elements, element);
+    } else if(component && !strcmp(parts[0], "dynamic")) {
+      component->dynamic = TRUE;
+    } else if(component && !strcmp(parts[0], "priority")) {
+      component->priority = strtol(parts[1], NULL, 10);
+    } else if(element && !strcmp(parts[0], "source")) {
+      element->endpoint = g_strdup(parts[1]);
+      element->ep_type = OCTOPUS_ENDPOINT_SOURCE;
+    } else if(element && !strcmp(parts[0], "sink")) {
+      element->endpoint = g_strdup(parts[1]);
+      element->ep_type = OCTOPUS_ENDPOINT_SINK;
+    } else if(element && !strcmp(parts[0], "parameter")) {
+      OctopusParameter *param;
+      param = g_new0(OctopusParameter, 1);
+      param->name = g_strdup(parts[1]);
+      param->value = g_strdup(parts[2]);
+      element->parameters = g_slist_prepend(element->parameters, param);
+    } else {
+      g_debug("Invalid keyword: %s", parts[0]);
+    }
+
+    g_strfreev(parts);
+  }
+
+  new_comps = g_slist_sort(new_comps, (GCompareFunc)component_priority_compare);
+
+  klass = OCTOPUS_BACKEND_GET_CLASS(backend);
+
+  for(iter1 = new_comps; iter1;) {
+    OctopusComponent *comp = (OctopusComponent *)iter1->data;
+    iter2 = iter1->next;
+    if(!klass->validate_component(backend, (OctopusComponent *)comp)) {
+      g_debug("Component %s is invalid", comp->name);
+      new_comps = g_slist_delete_link(new_comps, iter1);
+    }
+    iter1 = iter2;
+  }
+  g_debug("%d components loaded and validated", g_slist_length(new_comps));
+
+  new_comps = g_slist_concat(new_comps, backend->components);
+
+  for(iter1 = new_comps; iter1 != backend->components; iter1 = iter1->next) {
+    for(iter2 = iter1->next; iter2; iter2 = iter2->next) {
+      OctopusComponent *comp1 = (OctopusComponent *)iter1->data;
+      OctopusComponent *comp2 = (OctopusComponent *)iter2->data;
+
+      if(klass->can_link_components(backend, comp1, comp2)) {
+        comp1->links = g_slist_insert_sorted(comp1->links, comp2, (GCompareFunc)component_priority_compare);
+        g_debug("Adding link '%s' -> '%s'", comp1->name, comp2->name);
+      } else if(klass->can_link_components(backend, comp2, comp1)) {
+        comp2->links = g_slist_insert_sorted(comp2->links, comp1, (GCompareFunc)component_priority_compare);
+        g_debug("Adding link '%s' -> '%s'", comp2->name, comp1->name);
+      }
+    }
+  }
+
+  backend->components = g_slist_sort(new_comps, (GCompareFunc)component_priority_compare);
+}
+
+void
+octopus_backend_load_components_from_directory(OctopusBackend *backend,
+                                               gchar          *dirname)
+{
+  GnomeVFSDirectoryHandle *handle;
+  GnomeVFSFileInfo        *info;
+  GnomeVFSResult          result;
+
+  result = gnome_vfs_directory_open(&handle, dirname, GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
+
+  if(result != GNOME_VFS_OK)
+    return;
+
+  g_debug("Loading components from directory %s", dirname);
+
+  info = gnome_vfs_file_info_new();
+
+  while((result = gnome_vfs_directory_read_next(handle, info)) == GNOME_VFS_OK) {
+    if((info->type & GNOME_VFS_FILE_TYPE_REGULAR)
+       && g_str_has_prefix(info->name, backend->name)
+       && g_str_has_suffix(info->name, ".ocd"))
+    {
+      gchar *filename;
+      filename = g_strconcat(dirname, "/", info->name, NULL);
+      octopus_backend_load_components(backend, filename);
+      g_free(filename);
+    }
+  }
+
+  g_free(info);
+}
+
+OctopusComponent *
+octopus_backend_get_component_by_endpoint(OctopusBackend      *backend,
+                                          gchar               *ep_name,
+                                          OctopusEndpointType ep_type)
+{
+  GSList *citer;
+  GSList *eiter;
+
+  for(citer = backend->components; citer; citer = citer->next) {
+    OctopusComponent *comp = (OctopusComponent *)citer->data;
+    for(eiter = comp->elements; eiter; eiter = eiter->next) {
+      OctopusElement *elem = (OctopusElement *)eiter->data;
+      if(elem->endpoint && g_str_has_prefix(elem->endpoint, ep_name))
+        if(elem->ep_type == ep_type || ep_type == OCTOPUS_ENDPOINT_ANY)
+          return comp;
+    }
+  }
+
+  return NULL;
+}
+
+GSList *
+octopus_backend_build_component_chain(OctopusBackend   *backend,
+                                      OctopusComponent *source,
+                                      const gchar      *sink)
+{
+  return build_component_chain_recursive(backend, source, sink);
+}
+
+gchar **
+octopus_backend_get_endpoints_by_type(OctopusBackend      *backend,
+                                      OctopusEndpointType type)
+{
+  gchar  **result;
+  gchar  **ptr;
+  GSList *citer;
+  GSList *eiter;
+
+  ptr = result = g_new0(gchar *, g_slist_length(backend->components)+1);
+
+  for(citer = backend->components; citer; citer = citer->next) {
+    OctopusComponent *comp = (OctopusComponent *)citer->data;
+    for(eiter = comp->elements; eiter; eiter = eiter->next) {
+      OctopusElement *elem = (OctopusElement *)eiter->data;
+      if(elem->ep_type==type)
+        *ptr++ = g_strdup(elem->endpoint);
+    }
+  }
+
+  return result;
+}
+
+gboolean
+octopus_backend_init_route(OctopusBackend *backend,
+                           OctopusRoute   *route)
+{
+  g_assert(OCTOPUS_IS_BACKEND(backend));
+  return OCTOPUS_BACKEND_GET_CLASS(backend)->init_route(backend, route);
+}
+
+void
+octopus_backend_destroy_route(OctopusBackend *backend,
+                              OctopusRoute   *route)
+{
+  g_assert(OCTOPUS_IS_BACKEND(backend));
+  return OCTOPUS_BACKEND_GET_CLASS(backend)->destroy_route(backend, route);
+}
+
+gboolean
+octopus_backend_play_route(OctopusBackend *backend,
+                           OctopusRoute   *route)
+{
+  g_assert(OCTOPUS_IS_BACKEND(backend));
+  return OCTOPUS_BACKEND_GET_CLASS(backend)->play_route(backend, route);
+}
+
+gboolean
+octopus_backend_pause_route(OctopusBackend *backend,
+                            OctopusRoute   *route)
+{
+  g_assert(OCTOPUS_IS_BACKEND(backend));
+  return OCTOPUS_BACKEND_GET_CLASS(backend)->pause_route(backend, route);
+}
+
+gboolean
+octopus_backend_stop_route(OctopusBackend *backend,
+                          OctopusRoute   *route)
+{
+  g_assert(OCTOPUS_IS_BACKEND(backend));
+  return OCTOPUS_BACKEND_GET_CLASS(backend)->stop_route(backend, route);
+}
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-backend.h b/src/octopus-backend.h
new file mode 100644 (file)
index 0000000..5374654
--- /dev/null
@@ -0,0 +1,144 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef _OCTOPUS_BACKEND_H_
+#define _OCTOPUS_BACKEND_H_
+
+#include <glib-object.h>
+#include "octopus-route.h"
+
+#define OCTOPUS_TYPE_BACKEND             \
+        (octopus_backend_get_type())
+#define OCTOPUS_BACKEND(obj)             \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), OCTOPUS_TYPE_BACKEND, OctopusBackend))
+#define OCTOPUS_IS_BACKEND(obj)          \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), OCTOPUS_TYPE_BACKEND))
+#define OCTOPUS_BACKEND_CLASS(klass)     \
+        (G_TYPE_CHECK_CLASS_CAST((klass), OCTOPUS_TYPE_BACKEND, OctopusBackendClass))
+#define OCTOPUS_IS_BACKEND_CLASS(klass)  \
+        (G_TYPE_CHECK_CLASS_TYPE((klass), OCTOPUS_TYPE_BACKEND))
+#define OCTOPUS_BACKEND_GET_CLASS(obj)   \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), OCTOPUS_TYPE_BACKEND, OctopusBackendClass))
+
+/* XXX How do I fwd decl a typedef? */
+struct _OctopusModuleManager;
+
+typedef struct _OctopusBackend        OctopusBackend;
+typedef struct _OctopusBackendClass   OctopusBackendClass;
+typedef struct _OctopusParameter      OctopusParameter;
+typedef struct _OctopusElement        OctopusElement;
+typedef struct _OctopusComponent      OctopusComponent;
+
+typedef enum
+{
+  OCTOPUS_ENDPOINT_NONE = 0,
+  OCTOPUS_ENDPOINT_SOURCE,
+  OCTOPUS_ENDPOINT_SINK,
+  OCTOPUS_ENDPOINT_ANY
+} OctopusEndpointType;
+
+struct _OctopusBackend {
+  GObject                      parent;
+
+  gchar                        *name;
+  struct _OctopusModuleManager *module_manager;
+  GSList                       *components;
+};
+
+struct _OctopusBackendClass {
+  GObjectClass parent;
+
+  gboolean     (*can_link_components)(OctopusBackend *,
+                                      OctopusComponent *,
+                                      OctopusComponent *);
+  gboolean     (*validate_component) (OctopusBackend *,
+                                      OctopusComponent *);
+  gboolean     (*init_route)         (OctopusBackend *,
+                                      OctopusRoute *);
+  void         (*destroy_route)      (OctopusBackend *,
+                                      OctopusRoute *);
+  gboolean     (*play_route)         (OctopusBackend *,
+                                      OctopusRoute *);
+  gboolean     (*pause_route)        (OctopusBackend *,
+                                      OctopusRoute *);
+  gboolean     (*stop_route)         (OctopusBackend *,
+                                      OctopusRoute *);
+};
+
+struct _OctopusParameter {
+  gchar *name;
+  gchar *value;
+};
+
+struct _OctopusElement {
+  gchar               *name;
+  gchar               *endpoint;
+  OctopusEndpointType ep_type;
+  GSList              *parameters;
+};
+
+struct _OctopusComponent {
+  gchar    *name;
+  GSList   *elements;
+  GSList   *links;
+  gboolean dynamic;
+  gint     priority;
+};
+
+GType
+octopus_backend_get_type                 (void);
+
+void
+octopus_backend_load_components          (OctopusBackend *backend,
+                                          gchar          *filename);
+void
+octopus_backend_load_components_from_directory(OctopusBackend *backend,
+                                          gchar          *dirname);
+OctopusComponent *
+octopus_backend_get_component_by_endpoint(OctopusBackend      *backend,
+                                          gchar               *ep_name,
+                                          OctopusEndpointType ep_type);
+GSList *
+octopus_backend_build_component_chain    (OctopusBackend   *backend,
+                                          OctopusComponent *source,
+                                          const gchar      *sink);
+
+gchar **
+octopus_backend_get_endpoints_by_type    (OctopusBackend      *backend,
+                                          OctopusEndpointType type);
+
+gboolean
+octopus_backend_init_route               (OctopusBackend *backend,
+                                          OctopusRoute   *route);
+void
+octopus_backend_destroy_route            (OctopusBackend *backend,
+                                          OctopusRoute   *route);
+gboolean
+octopus_backend_play_route               (OctopusBackend *backend,
+                                          OctopusRoute   *route);
+gboolean
+octopus_backend_pause_route              (OctopusBackend *backend,
+                                          OctopusRoute   *route);
+gboolean
+octopus_backend_stop_route               (OctopusBackend *backend,
+                                          OctopusRoute   *route);
+
+#endif
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-dbus.xml b/src/octopus-dbus.xml
new file mode 100644 (file)
index 0000000..5ae3ec6
--- /dev/null
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<node name="/com/ixs/OctopusService">
+  <interface name="com.ixs.OctopusService">
+    <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="octopus_server" />
+
+    <!-- Endpoints (modules) -->
+
+    <!-- Fetches the names of both source modules and destination modules -->
+    <method name="GetEndpoints">
+      <arg type="as" direction="out" name="sources" />
+      <arg type="as" direction="out" name="destinations" />
+    </method>
+
+    <!-- Fetches the names of source modules -->
+    <method name="GetSources">
+      <arg type="as" direction="out" name="sources" />
+    </method>
+
+    <!-- Fetches the names of destination modules -->
+    <method name="GetDestinations">
+      <arg type="as" direction="out" name="destinations" />
+    </method>
+
+    <!-- Fetches human-readable descriptions of specified modules -->
+    <!--<method name="GetDescriptions">
+      <arg type="as" direction="in" name="endpoints" />
+      <arg type="as" direction="out" name="descriptions" />
+    </method>-->
+
+    <!-- Module Manager API -->
+
+<!--
+    <method name="GetSrcModules">
+      <arg type="as" direction="out" />
+    </method>
+
+    <method name="GetDestModules">
+      <arg type="as" direction="out" />
+    </method>
+
+    <method name="GetActiveRoutes">
+      <arg type="as" direction="out" />
+    </method>
+-->
+
+    <method name="New">
+      <arg type="u" direction="out" />
+    </method>
+
+    <method name="Release">
+      <arg type="u" direction="in" />
+    </method>
+
+    <!-- Set the endpoint mapping of a pipeline ([source1, ...] => [dest1, ...]) -->
+    <method name="SetMapping">
+      <arg type="u" direction="in" name="id" />
+      <arg type="as" direction="in" name="sources" />
+      <arg type="as" direction="in" name="destinations" />
+    </method>
+
+    <!-- Get the endpoint mapping of a pipeline ([source1, ...] => [dest1, ...]) -->
+    <method name="GetMapping">
+      <arg type="u" direction="in" name="id" />
+      <arg type="as" direction="out" name="sources" />
+      <arg type="as" direction="out" name="destinations" />
+    </method>
+    
+    <!-- Set an URI for an endpoint -->
+    <method name="SetEndpointUri">
+      <arg type="u" direction="in" name="id" />
+      <arg type="s" direction="in" name="endpoint" />
+      <arg type="s" direction="in" name="uri" />
+    </method>
+
+    <!-- Get an URI for an endpoint -->
+    <method name="GetEndpointUri">
+      <arg type="u" direction="in" name="id" />
+      <arg type="s" direction="in" name="endpoint" />
+      <arg type="s" direction="out" name="uri" />
+    </method>
+
+ <!-- Set the display name and window xid -->
+    <method name="SetX11Display">
+      <arg type="u" direction="in" name="id" />
+      <arg type="s" direction="in" name="endpoint" />
+      <arg type="s" direction="in" name="display" />
+      <arg type="u" direction="in" name="window" />
+    </method>
+
+    <!-- Media API  -->
+
+    <method name="Play">
+      <arg type="u" name="id" direction="in" />
+    </method>
+
+    <method name="Stop">
+      <arg type="u" name="id" direction="in" />
+    </method>
+
+    <method name="Pause">
+      <arg type="u" name="id" direction="in" />
+    </method>
+
+    <!-- Playback signals -->
+
+    <!-- Emitted when the playback fails  -->
+    <signal name="PlaybackError">
+      <arg type="u" name="id" direction="out"/>
+      <arg type="s" name="message" direction="out"/>
+    </signal>
+
+    <!-- Emitted once per second, detailing number of seconds from the start
+         and total length of the stream (if available, length=0 if not).
+         The number is not to be treated as microsecond-accurate, but more as
+         a user-visible hint of the duration.
+      -->
+    <signal name="StreamPosition">
+      <arg type="u" name="id" direction="out"/>
+      <arg type="u" name="current" direction="out"/>
+      <arg type="u" name="length" direction="out"/>
+    </signal>
+
+    <!-- Stream state changed
+         TBD: Define the state values (stopped, playing, paused, etc)
+      -->
+    <signal name="StreamStateChanged">
+      <arg type="u" name="id" direction="out"/>
+      <arg type="u" name="state" direction="out"/>
+    </signal>
+  </interface>
+</node>
diff --git a/src/octopus-marshals.spec b/src/octopus-marshals.spec
new file mode 100644 (file)
index 0000000..a5365a4
--- /dev/null
@@ -0,0 +1,3 @@
+VOID:UINT,UINT
+VOID:UINT,STRING
+VOID:UINT,UINT,UINT
diff --git a/src/octopus-module-manager.c b/src/octopus-module-manager.c
new file mode 100644 (file)
index 0000000..79325bf
--- /dev/null
@@ -0,0 +1,196 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gmodule.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include "octopus-module-manager.h"
+
+struct _OctopusModuleManagerPrivate {
+  GSList *modules;
+};
+
+static void
+octopus_module_manager_class_init(OctopusModuleManagerClass *klass);
+static void
+octopus_module_manager_init      (OctopusModuleManager *module_manager);
+static void
+octopus_module_manager_finalize  (GObject *object);
+static gboolean
+load_modules_from_directory      (OctopusModuleManager *object,
+                                  const gchar          *dir_name);
+
+/*
+ * GObject functions
+ */
+
+G_DEFINE_TYPE(OctopusModuleManager, octopus_module_manager, G_TYPE_OBJECT);
+
+static void
+octopus_module_manager_class_init(OctopusModuleManagerClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS(klass);
+
+  gnome_vfs_init();
+
+  gobject_class->finalize = octopus_module_manager_finalize;
+}
+
+static void
+octopus_module_manager_init(OctopusModuleManager *module_manager)
+{
+  OctopusModuleManagerPrivate *priv;
+
+  priv = module_manager->priv = g_new0(OctopusModuleManagerPrivate, 1);
+
+  load_modules_from_directory (module_manager, PLUGINDIR "/");
+}
+
+static void
+octopus_module_manager_finalize(GObject *object)
+{
+  OctopusModuleManager        *manager;
+  OctopusModuleManagerPrivate *priv;
+  GSList                      *iter;
+
+  manager = OCTOPUS_MODULE_MANAGER(object);
+  priv = manager->priv;
+
+  for(iter = priv->modules; iter; iter = iter->next)
+    g_object_unref(iter->data);
+  g_slist_free(priv->modules);
+  g_free(priv);
+}
+
+/*
+ * Private functions
+ */
+
+static void
+load_module (OctopusModuleManager *module_manager,
+             const gchar          *filename)
+{
+  g_assert(OCTOPUS_IS_MODULE_MANAGER(module_manager));
+  g_assert(filename != NULL);
+
+  if(g_module_supported ()) {
+    GModule *gmod;
+    OctopusModuleInitFunc init = NULL;
+
+    if((gmod = g_module_open(filename, 0))
+      && g_module_symbol(gmod, "octopus_module_init", (gpointer)&init)) {
+      if(init(module_manager)) {
+        g_debug("Module '%s' successfully loaded", filename);
+      } else {
+        g_warning("Module '%s' failed to initialize", filename);
+        g_module_close(gmod);
+      }
+    } else {
+      g_warning("Unable to load module '%s': %s", filename, g_module_error());
+      if(gmod)
+        g_module_close(gmod);
+    }
+
+    /* XXX Store the handle somewhere for later closing? */
+  }
+}
+
+static gboolean
+load_modules_from_directory (OctopusModuleManager *module_manager,
+                             const gchar          *dir_name)
+{
+  GnomeVFSDirectoryHandle     *handle;
+  OctopusModuleManagerPrivate *priv;
+  GnomeVFSFileInfo            *info;
+  GnomeVFSResult              result;
+
+  g_assert(OCTOPUS_IS_MODULE_MANAGER (module_manager));
+  g_assert(dir_name != NULL);
+
+  priv = module_manager->priv;
+
+  result = gnome_vfs_directory_open(&handle, dir_name, GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
+
+  if(result != GNOME_VFS_OK)
+    return FALSE;
+
+  g_debug("Loading modules from directory %s", dir_name);
+
+  info = gnome_vfs_file_info_new();
+
+  while((result = gnome_vfs_directory_read_next(handle, info)) == GNOME_VFS_OK) {
+    if(info->type & GNOME_VFS_FILE_TYPE_REGULAR) {
+      gchar *filename;
+      filename = g_strconcat(dir_name, info->name, NULL);
+      load_module(module_manager, filename);
+      g_free(filename);
+    }
+  }
+
+  g_free(info);
+
+  return TRUE;
+}
+
+/*
+ * Public API
+ */
+
+void
+octopus_module_manager_register_module(OctopusModuleManager *manager,
+                                       OctopusModule        *module)
+{
+  manager->priv->modules = g_slist_prepend(manager->priv->modules, module);
+}
+
+OctopusModule *
+octopus_module_manager_get_module(OctopusModuleManager *manager,
+                                  const gchar          *name)
+{
+  GSList        *iter;
+  OctopusModule *module = NULL;
+
+  for(iter = manager->priv->modules; (!module && iter); iter = iter->next) {
+    gchar *mod_name;
+
+    g_object_get(G_OBJECT(iter->data), "name", &mod_name, NULL);
+    if(!strcmp(mod_name, name))
+      module = (OctopusModule *)iter->data;
+    g_free(mod_name);
+  }
+
+  return module;
+}
+
+
+/* Emacs indentatation information
+   Local Variables:
+   indent-tabs-mode:nil
+   tab-width:2
+   c-set-offset:2
+   c-basic-offset:2
+   End:
+*/
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
+
diff --git a/src/octopus-module-manager.h b/src/octopus-module-manager.h
new file mode 100644 (file)
index 0000000..e5f3c6a
--- /dev/null
@@ -0,0 +1,82 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef _OCTOPUS_MODULE_MANAGER_H_
+#define _OCTOPUS_MODULE_MANAGER_H_
+
+#include <glib-object.h>
+#include "octopus-backend.h"
+#include "octopus-module.h"
+
+G_BEGIN_DECLS
+
+#define OCTOPUS_TYPE_MODULE_MANAGER             \
+             (octopus_module_manager_get_type ())
+#define OCTOPUS_MODULE_MANAGER(obj)             \
+        (G_TYPE_CHECK_INSTANCE_CAST ((obj), OCTOPUS_TYPE_MODULE_MANAGER, OctopusModuleManager))
+#define OCTOPUS_MODULE_MANAGER_CLASS(klass)     \
+        (G_TYPE_CHECK_CLASS_CAST ((klass), OCTOPUS_TYPE_MODULE_MANAGER, OctopusModuleManagerClass))
+#define OCTOPUS_IS_MODULE_MANAGER(obj)          \
+        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OCTOPUS_TYPE_MODULE_MANAGER))
+#define OCTOPUS_IS_MODULE_MANAGER_CLASS(klass)  \
+        (G_TYPE_CHECK_CLASS_TYPE ((klass), OCTOPUS_TYPE_MODULE_MANAGER))
+#define OCTOPUS_MODULE_MANAGER_GET_CLASS(obj)   \
+        (G_TYPE_INSTANCE_GET_CLASS ((obj), OCTOPUS_TYPE_MODULE_MANAGER, OctopusModuleManagerClass))
+
+typedef struct _OctopusModuleManager        OctopusModuleManager;
+typedef struct _OctopusModuleManagerClass   OctopusModuleManagerClass;
+typedef struct _OctopusModuleManagerPrivate OctopusModuleManagerPrivate;
+
+typedef gboolean (*OctopusModuleInitFunc)(OctopusModuleManager *);
+
+struct _OctopusModuleManager {
+  GObject parent;
+
+  OctopusModuleManagerPrivate *priv;
+};
+
+struct _OctopusModuleManagerClass {
+  GObjectClass parent;
+};
+
+/* Public API */
+
+GType
+octopus_module_manager_get_type       (void);
+
+void
+octopus_module_manager_register_module(OctopusModuleManager *manager,
+                                       OctopusModule        *module);
+OctopusModule *
+octopus_module_manager_get_module     (OctopusModuleManager *manager,
+                                       const gchar          *name);
+
+G_END_DECLS
+
+#endif
+
+/* Emacs indentatation information
+   Local Variables:
+   indent-tabs-mode:nil
+   tab-width:2
+   c-set-offset:2
+   c-basic-offset:2
+   End:
+*/
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-module.c b/src/octopus-module.c
new file mode 100644 (file)
index 0000000..82273e9
--- /dev/null
@@ -0,0 +1,68 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include "octopus-module.h"
+
+static void 
+get_property(GObject    *object,
+             guint      prop_id,
+             GValue     *value,
+             GParamSpec *pspec);
+
+G_DEFINE_TYPE(OctopusModule, octopus_module, G_TYPE_OBJECT)
+
+static void
+octopus_module_class_init(OctopusModuleClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS(klass);
+
+  gobject_class->get_property = get_property;
+}
+
+static void
+octopus_module_init(OctopusModule *module)
+{
+       module->name = NULL;
+}
+
+static void 
+get_property(GObject    *object,
+             guint      prop_id,
+             GValue     *value,
+             GParamSpec *pspec)
+{
+  OctopusModule *module;
+
+  g_assert(OCTOPUS_IS_MODULE(object));
+  module = (OctopusModule *)object;
+
+  switch(prop_id)
+  {
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+  };
+}
+
+/*
+ * Public API
+ */
+
+/* To be done */
diff --git a/src/octopus-module.h b/src/octopus-module.h
new file mode 100644 (file)
index 0000000..6f3320b
--- /dev/null
@@ -0,0 +1,58 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef _OCTOPUS_MODULE_H_
+#define _OCTOPUS_MODULE_H_
+
+#include "octopus-route.h"
+
+#define OCTOPUS_TYPE_MODULE             \
+        (octopus_module_get_type())
+#define OCTOPUS_MODULE(obj)             \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), OCTOPUS_TYPE_MODULE, OctopusModule))
+#define OCTOPUS_IS_MODULE(obj)          \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), OCTOPUS_TYPE_MODULE))
+#define OCTOPUS_MODULE_CLASS(klass)     \
+        (G_TYPE_CHECK_CLASS_CAST((klass), OCTOPUS_TYPE_MODULE, OctopusModuleClass))
+#define OCTOPUS_IS_MODULE_CLASS(klass)  \
+        (G_TYPE_CHECK_CLASS_TYPE((klass), OCTOPUS_TYPE_MODULE))
+#define OCTOPUS_MODULE_GET_CLASS(obj)   \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), OCTOPUS_TYPE_MODULE, OctopusModuleClass))
+
+typedef struct _OctopusModule        OctopusModule;
+typedef struct _OctopusModuleClass   OctopusModuleClass;
+
+struct _OctopusModule {
+  GObject parent;
+
+  gchar   *name;
+};
+
+struct _OctopusModuleClass {
+  GObjectClass parent;
+};
+
+GType
+octopus_module_get_type         (void);
+
+/* Module API TBD */
+
+#endif
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-route.c b/src/octopus-route.c
new file mode 100644 (file)
index 0000000..d78c593
--- /dev/null
@@ -0,0 +1,382 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include "octopus-backend.h"
+#include "octopus-route.h"
+#include "octopus-marshals.h"
+
+enum
+{
+  PROP_ID = 1,
+  PROP_BACKEND,
+  PROP_SOURCES,
+  PROP_SINKS
+};
+
+enum
+{
+  SIGNAL_MAPPING_CHANGED,
+  SIGNAL_ENDPOINT_CHANGED,
+  SIGNAL_PLAYBACK_ERROR,
+  SIGNAL_STREAM_POSITION,
+  SIGNAL_STATE_CHANGED,
+  N_SIGNALS
+};
+
+struct _OctopusRoutePrivate
+{
+  guint          id;
+  OctopusBackend *backend;
+  GHashTable     *endpoints;
+  gchar          **sources;
+  gchar          **sinks;
+};
+
+static void
+octopus_route_finalize(GObject *route);
+static void
+set_property(GObject      *object,
+             guint        prop_id,
+             const GValue *value,
+             GParamSpec   *pspec);
+static void
+get_property(GObject    *object,
+             guint      prop_id,
+             GValue     *value,
+             GParamSpec *pspec);
+
+OctopusEndpoint *
+get_or_create_endpoint(OctopusRoute *route,
+                       const gchar  *ep_name);
+void
+free_endpoint(OctopusEndpoint *endpoint);
+
+static guint signals[N_SIGNALS];
+
+G_DEFINE_TYPE(OctopusRoute, octopus_route, G_TYPE_OBJECT)
+
+static void
+octopus_route_class_init(OctopusRouteClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS(klass);
+
+  gobject_class->finalize = octopus_route_finalize;
+  gobject_class->set_property = set_property;
+  gobject_class->get_property = get_property;
+
+  g_object_class_install_property(gobject_class, PROP_ID,
+    g_param_spec_uint("id", "ID", "Route ID (used for the D-Bus API)", 0, UINT_MAX, 0,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property(gobject_class, PROP_BACKEND,
+    g_param_spec_pointer("backend", "Backend", "The backend that created this route",
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property(gobject_class, PROP_SOURCES,
+    g_param_spec_pointer("sources", "Sources", "Source endpoints used for this route",
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property(gobject_class, PROP_SINKS,
+    g_param_spec_pointer("sinks", "Sinks", "Sink endpoints used for this route",
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  signals[SIGNAL_MAPPING_CHANGED] = 
+        g_signal_new ("mapping-changed",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
+
+  signals[SIGNAL_ENDPOINT_CHANGED] = 
+        g_signal_new ("endpoint-changed",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__POINTER,
+                      G_TYPE_NONE,
+                      1, G_TYPE_POINTER);
+
+  signals[SIGNAL_PLAYBACK_ERROR] = 
+        g_signal_new ("playback-error",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__STRING,
+                      G_TYPE_NONE,
+                      1, G_TYPE_STRING);
+
+  signals[SIGNAL_STREAM_POSITION] = 
+        g_signal_new ("stream-position",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_user_marshal_VOID__UINT_UINT,
+                      G_TYPE_NONE,
+                      2, G_TYPE_UINT, G_TYPE_UINT);
+
+  signals[SIGNAL_STATE_CHANGED] = 
+        g_signal_new ("state-changed",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__UINT,
+                      G_TYPE_NONE,
+                      1, G_TYPE_UINT);
+}
+
+static void
+octopus_route_init(OctopusRoute *route)
+{
+  route->priv = g_new0(OctopusRoutePrivate, 1);
+
+  route->priv->endpoints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)free_endpoint);
+}
+
+static void
+octopus_route_finalize(GObject *object)
+{
+  OctopusRoute *route;
+
+  route = OCTOPUS_ROUTE(object);
+
+  octopus_backend_destroy_route(route->priv->backend, route);
+  g_hash_table_destroy(route->priv->endpoints);
+}
+
+static void
+set_property(GObject      *object,
+             guint        prop_id,
+             const GValue *value,
+             GParamSpec   *pspec)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_ROUTE(object));
+  route = (OctopusRoute *)object;
+
+  switch(prop_id)
+  {
+  case PROP_ID:
+    route->priv->id = g_value_get_uint(value);
+    break;
+  case PROP_BACKEND:
+    if(route->priv->backend) {
+      g_warning("Route already has a backend");
+    } else {
+      route->priv->backend = g_value_get_pointer(value);
+      octopus_backend_init_route(route->priv->backend, route);
+    }
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+  }
+}
+
+static void
+get_property(GObject    *object,
+             guint      prop_id,
+             GValue     *value,
+             GParamSpec *pspec)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_ROUTE(object));
+  route = (OctopusRoute *)object;
+
+  switch(prop_id)
+  {
+  case PROP_ID:
+    g_value_set_uint(value, route->priv->id);
+    break;
+  case PROP_BACKEND:
+    g_value_set_pointer(value, route->priv->backend);
+    break;
+  case PROP_SOURCES:
+    g_value_set_pointer(value, route->priv->sources);
+    break;
+  case PROP_SINKS:
+    g_value_set_pointer(value, route->priv->sinks);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+  }
+}
+
+/*
+ * Helper functions
+ */
+
+OctopusEndpoint *
+get_or_create_endpoint(OctopusRoute *route,
+                       const gchar  *ep_name)
+{
+  OctopusEndpoint *ep;
+
+  ep = g_hash_table_lookup(route->priv->endpoints, ep_name);
+  if(!ep) {
+    ep = g_new0(OctopusEndpoint, 1);
+    ep->name = g_strdup(ep_name);
+    g_hash_table_insert(route->priv->endpoints, g_strdup(ep_name), ep);
+  }
+
+  return ep;
+}
+
+void
+free_endpoint(OctopusEndpoint *endpoint)
+{
+  g_free(endpoint->name);
+  g_free(endpoint->uri);
+  g_free(endpoint->display);
+  g_free(endpoint);
+}
+
+static gboolean
+fuzzy_name_compare(gpointer key,
+                   gpointer value,
+                   gpointer user_data)
+{
+  return g_str_has_prefix(user_data, key);
+}
+
+/*
+ * Public API
+ */
+
+gboolean
+octopus_route_set_mapping              (OctopusRoute *route,
+                                        const gchar  *const *sources,
+                                        const gchar  *const *sinks)
+{
+  OctopusRoutePrivate *priv = route->priv;
+  gchar *src_str;
+  gchar *sink_str;
+
+  g_strfreev(priv->sources);
+  g_strfreev(priv->sinks);
+  priv->sources = g_strdupv((gchar **)sources);
+  priv->sinks = g_strdupv((gchar **)sinks);
+
+  src_str=g_strjoinv(", ", priv->sources);
+  sink_str=g_strjoinv(", ", priv->sinks);
+  g_debug("Set route mapping to [%s] -> [%s]", src_str, sink_str);
+  g_free(src_str);
+  g_free(sink_str);
+
+  g_signal_emit(route, signals[SIGNAL_MAPPING_CHANGED], 0);
+
+  return TRUE;
+}
+
+gboolean
+octopus_route_set_endpoint_uri(OctopusRoute *route,
+                               const gchar  *ep_name,
+                               const gchar  *uri)
+{
+  OctopusEndpoint *ep;
+
+  ep = get_or_create_endpoint(route, ep_name);
+  g_free(ep->uri);
+  ep->uri = g_strdup(uri);
+
+  g_debug("Stored URI '%s' for endpoint '%s'", uri, ep_name);
+
+  g_signal_emit(route, signals[SIGNAL_ENDPOINT_CHANGED], 0, ep);
+
+  return TRUE;
+}
+
+gboolean
+octopus_route_set_endpoint_x11_display (OctopusRoute *route,
+                                 const gchar *ep_name,
+                                 const gchar *display,
+                                 gulong      window)
+{
+  OctopusEndpoint *ep;
+
+  ep = get_or_create_endpoint(route, ep_name);
+  g_free(ep->display);
+  ep->display = g_strdup(display);
+  ep->window = window;
+
+  g_debug("Stored display ('%s', %lx) for endpoint '%s'", display, window, ep_name);
+
+  g_signal_emit(route, signals[SIGNAL_ENDPOINT_CHANGED], 0, ep);
+
+  return TRUE;
+}
+
+const OctopusEndpoint *
+octopus_route_get_endpoint(OctopusRoute *route,
+                           const gchar  *ep_name)
+{
+  return g_hash_table_find(route->priv->endpoints, fuzzy_name_compare, (gpointer)ep_name);
+}
+
+gboolean
+octopus_route_play(OctopusRoute *route)
+{
+  return octopus_backend_play_route(route->priv->backend, route);
+}
+
+gboolean
+octopus_route_pause(OctopusRoute *route)
+{
+  return octopus_backend_pause_route(route->priv->backend, route);
+}
+
+gboolean
+octopus_route_stop(OctopusRoute *route)
+{
+  return octopus_backend_stop_route(route->priv->backend, route);
+}
+
+void
+octopus_route_playback_error(OctopusRoute *route,
+                             const gchar  *error)
+{
+  g_signal_emit(route, signals[SIGNAL_PLAYBACK_ERROR], 0, error);
+}
+
+void
+octopus_route_stream_position(OctopusRoute *route,
+                              guint        current,
+                              guint        length)
+{
+  g_signal_emit(route, signals[SIGNAL_STREAM_POSITION], 0, current, length);
+}
+
+void
+octopus_route_state_changed(OctopusRoute      *route,
+                            OctopusRouteState state)
+{
+  g_signal_emit(route, signals[SIGNAL_STATE_CHANGED], 0, state);
+}
+
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-route.h b/src/octopus-route.h
new file mode 100644 (file)
index 0000000..4ff978a
--- /dev/null
@@ -0,0 +1,109 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef _OCTOPUS_ROUTE_H_
+#define _OCTOPUS_ROUTE_H_
+
+#include <glib-object.h>
+
+#define OCTOPUS_TYPE_ROUTE             \
+        (octopus_route_get_type())
+#define OCTOPUS_ROUTE(obj)             \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), OCTOPUS_TYPE_ROUTE, OctopusRoute))
+#define OCTOPUS_IS_ROUTE(obj)          \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), OCTOPUS_TYPE_ROUTE))
+#define OCTOPUS_ROUTE_CLASS(klass)     \
+        (G_TYPE_CHECK_CLASS_CAST((klass), OCTOPUS_TYPE_ROUTE, OctopusRouteClass))
+#define OCTOPUS_IS_ROUTE_CLASS(klass)  \
+        (G_TYPE_CHECK_CLASS_TYPE((klass), OCTOPUS_TYPE_ROUTE))
+#define OCTOPUS_ROUTE_GET_CLASS(obj)   \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), OCTOPUS_TYPE_ROUTE, OctopusRouteClass))
+
+typedef struct _OctopusRoute        OctopusRoute;
+typedef struct _OctopusRouteClass   OctopusRouteClass;
+typedef struct _OctopusRoutePrivate OctopusRoutePrivate;
+typedef struct _OctopusEndpoint     OctopusEndpoint;
+
+typedef enum
+{
+  OCTOPUS_ROUTE_STOPPED,
+  OCTOPUS_ROUTE_PAUSED,
+  OCTOPUS_ROUTE_PLAYING
+} OctopusRouteState;
+
+struct _OctopusRoute
+{
+  GObject             parent;
+  OctopusRoutePrivate *priv;
+};
+
+struct _OctopusRouteClass
+{
+  GObjectClass parent;
+};
+
+struct _OctopusEndpoint
+{
+  gchar  *name;
+  gchar  *uri;
+  gchar  *display;
+  gulong window;
+};
+
+GType
+octopus_route_get_type                 (void);
+
+gboolean
+octopus_route_set_mapping              (OctopusRoute *route,
+                                        const gchar  *const *sources,
+                                        const gchar  *const *sinks);
+gboolean
+octopus_route_set_endpoint_uri         (OctopusRoute *route,
+                                        const gchar  *ep_name,
+                                        const gchar  *uri);
+gboolean
+octopus_route_set_endpoint_x11_display (OctopusRoute *route,
+                                        const gchar *ep_name,
+                                        const gchar *display,
+                                        gulong      window);
+const OctopusEndpoint *
+octopus_route_get_endpoint             (OctopusRoute *route,
+                                        const gchar  *ep_name);
+
+gboolean
+octopus_route_play                     (OctopusRoute *route);
+gboolean
+octopus_route_pause                    (OctopusRoute *route);
+gboolean
+octopus_route_stop                     (OctopusRoute *route);
+
+void
+octopus_route_playback_error           (OctopusRoute *route,
+                                        const gchar  *error);
+void
+octopus_route_stream_position          (OctopusRoute *route,
+                                        guint        current,
+                                        guint        length);
+void
+octopus_route_state_changed            (OctopusRoute      *route,
+                                        OctopusRouteState state);
+
+#endif
+
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
diff --git a/src/octopus-server.c b/src/octopus-server.c
new file mode 100644 (file)
index 0000000..58ad301
--- /dev/null
@@ -0,0 +1,537 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include "octopus-server.h"
+#include "octopus-backend-gst.h"
+#include "octopus-route.h"
+#include "octopus-module-manager.h"
+#include "octopus-glue.h"
+#include "octopus-marshals.h"
+
+#define OCTOPUS_SERVER_ERROR g_quark_from_static_string("octopus-server")
+#define OCTOPUS_SERVER_ERROR_FAILED 1
+#define OCTOPUS_SERVER_ERROR_UNKNOWN_ROUTE 2
+
+enum {
+  SIGNAL_PLAYBACK_ERROR,
+  SIGNAL_STREAM_POSITION,
+  SIGNAL_STREAM_STATE_CHANGED,
+  N_SIGNALS
+};
+
+struct _OctopusServerPrivate {
+  DBusGConnection      *dbus_connection;
+  OctopusModuleManager *module_manager;
+  GHashTable           *routes;
+  OctopusBackend       *backend;
+};
+
+static void
+octopus_server_finalize     (GObject *object);
+
+static void
+route_playback_error(OctopusRoute *route,
+                     const gchar  *error,
+                     gpointer     user_data);
+static void
+route_stream_position(OctopusRoute *route,
+                      guint        pos,
+                      guint        dur,
+                      gpointer     user_data);
+static void
+route_state_changed(OctopusRoute      *route,
+                    OctopusRouteState state,
+                    gpointer          user_data);
+
+static guint next_route_id = 1;
+static guint signals[N_SIGNALS];
+
+/* GObject stuff */
+
+G_DEFINE_TYPE(OctopusServer, octopus_server, G_TYPE_OBJECT)
+
+static void
+octopus_server_class_init(OctopusServerClass *klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *)klass;
+
+  gobject_class->finalize = octopus_server_finalize;
+
+  dbus_g_object_type_install_info(OCTOPUS_TYPE_SERVER, &dbus_glib_octopus_server_object_info);
+
+  signals[SIGNAL_PLAYBACK_ERROR] = 
+        g_signal_new ("playback-error",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_user_marshal_VOID__UINT_STRING,
+                      G_TYPE_NONE,
+                      2, G_TYPE_UINT, G_TYPE_STRING);
+
+  signals[SIGNAL_STREAM_POSITION] = 
+        g_signal_new ("stream-position",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_user_marshal_VOID__UINT_UINT_UINT,
+                      G_TYPE_NONE,
+                      3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+
+  signals[SIGNAL_STREAM_STATE_CHANGED] = 
+        g_signal_new ("stream-state-changed",
+                      G_TYPE_FROM_CLASS(gobject_class),
+                      G_SIGNAL_NO_RECURSE,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_user_marshal_VOID__UINT_UINT,
+                      G_TYPE_NONE,
+                      2, G_TYPE_UINT, G_TYPE_UINT);
+}
+
+static void
+octopus_server_init(OctopusServer *server)
+{
+  OctopusServerPrivate *priv;
+
+  priv = server->priv = g_new0(OctopusServerPrivate, 1);
+
+  priv->module_manager = OCTOPUS_MODULE_MANAGER(g_object_new(OCTOPUS_TYPE_MODULE_MANAGER, NULL));
+  /* XXX Put backends to modules?  Or not? */
+  priv->backend = OCTOPUS_BACKEND(g_object_new(OCTOPUS_TYPE_BACKEND_GST, "module-manager", priv->module_manager, NULL));
+  octopus_backend_load_components_from_directory(priv->backend, COMPONENTDIR);
+  priv->routes = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_object_unref);
+}
+
+static void
+octopus_server_finalize(GObject *object)
+{
+  OctopusServer        *server;
+  OctopusServerPrivate *priv;
+
+  server = OCTOPUS_SERVER(object);
+  priv = server->priv;
+
+  g_hash_table_destroy(priv->routes);
+  g_object_unref(priv->backend);
+  g_object_unref(priv->module_manager);
+
+  g_free(priv);
+}
+
+/*
+ * Public API
+ */
+
+gboolean
+octopus_server_register_service(OctopusServer *server)
+{
+  OctopusServerPrivate    *priv;
+  DBusGConnection         *conn;
+  DBusGProxy              *proxy;
+  GError                  *error = NULL;
+  guint                   request_ret;
+
+  g_assert(OCTOPUS_IS_SERVER(server));
+
+  priv = server->priv;
+
+  /* Init the D-BUS connection */
+  g_debug("Initializing D-BUS connection");
+  conn = priv->dbus_connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+  if(error) {
+    g_critical("Failed to open connection to session bus: %s\n", error->message);
+    g_error_free(error);
+
+    return FALSE;
+  }
+
+  /* Register the D-BUS path */
+  g_debug("Registering the D-BUS path");
+  dbus_g_connection_register_g_object(conn, DBUS_IXS_OCTOPUS_PATH, G_OBJECT(server));
+
+  proxy = dbus_g_proxy_new_for_name(conn, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus");
+
+  if(!proxy) {
+    g_critical("Couldn't create proxy object");
+    dbus_g_connection_unref(conn);
+    priv->dbus_connection = NULL;
+
+    return FALSE;
+  }
+
+  if(!org_freedesktop_DBus_request_name(proxy, DBUS_IXS_OCTOPUS_SERVICE, 0, &request_ret, &error))
+  {
+    g_critical("Unable to register service: %s\n", error->message);
+    g_error_free(error);
+    dbus_g_connection_unref(conn);
+    priv->dbus_connection = NULL;
+    g_object_unref(proxy);
+
+    return FALSE;
+  }
+
+  g_object_unref(proxy);
+
+  return TRUE;
+}
+
+/*
+ * Helper functions
+ */
+
+static OctopusRoute *
+get_route_by_id(OctopusServer *server,
+                guint id,
+                GError **error)
+{
+  OctopusServerPrivate *priv = server->priv;
+  OctopusRoute         *route;
+
+  route = g_hash_table_lookup(priv->routes, &id);
+  if(!route) {
+    g_debug("Route %d not found", id);
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_UNKNOWN_ROUTE, "Unknown route");
+    return NULL;
+  }
+
+  return route;
+}
+
+/*
+ * D-Bus API functions
+ */
+
+gboolean
+octopus_server_get_endpoints(OctopusServer   *server,
+                             gchar           ***sources,
+                             gchar           ***dests,
+                             GError          **error)
+{
+  *sources = octopus_backend_get_endpoints_by_type(server->priv->backend, OCTOPUS_ENDPOINT_SOURCE);
+  *dests = octopus_backend_get_endpoints_by_type(server->priv->backend, OCTOPUS_ENDPOINT_SINK);
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_get_sources(OctopusServer   *server,
+                           gchar           ***sources,
+                           GError          **error)
+{
+  *sources = octopus_backend_get_endpoints_by_type(server->priv->backend, OCTOPUS_ENDPOINT_SOURCE);
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_get_destinations(OctopusServer   *server,
+                                gchar           ***dests,
+                                GError          **error)
+{
+  *dests = octopus_backend_get_endpoints_by_type(server->priv->backend, OCTOPUS_ENDPOINT_SINK);
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_new(OctopusServer   *server,
+                   guint32         *id,
+                   GError          **error)
+{
+  OctopusRoute *route;
+  guint        *key;
+
+  g_assert(OCTOPUS_IS_SERVER(server));
+
+  /* Get a new ID for the route */
+  key = g_new0(guint, 1);
+  *key = next_route_id++;
+
+  route = g_object_new(OCTOPUS_TYPE_ROUTE, "backend", server->priv->backend, "id", *key, NULL);
+  if(!route) {
+    g_free(key);
+    g_debug("Could not create a new route");
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not create route");
+    return FALSE;
+  }
+
+  g_signal_connect(G_OBJECT(route), "playback-error", G_CALLBACK(route_playback_error), server);
+  g_signal_connect(G_OBJECT(route), "stream-position", G_CALLBACK(route_stream_position), server);
+  g_signal_connect(G_OBJECT(route), "state-changed", G_CALLBACK(route_state_changed), server);
+
+  g_hash_table_insert(server->priv->routes, key, route);
+
+  g_debug("Route %d created", *key);
+
+  *id = *key;
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_release(OctopusServer   *server,
+                       guint32         id,
+                       GError          **error)
+{
+  g_assert(OCTOPUS_IS_SERVER(server));
+
+  if(!g_hash_table_remove(server->priv->routes, &id)) {
+    g_debug("Route %d not found", id);
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_UNKNOWN_ROUTE, "Unknown route");
+    return FALSE;
+  }
+
+  g_debug("Route %d released", id);
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_set_mapping(OctopusServer   *server,
+                           guint32         id,
+                           const gchar     **srcs,
+                           const gchar     **dsts,
+                           GError          **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!octopus_route_set_mapping(route, srcs, dsts)) {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not set route mapping");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_get_mapping(OctopusServer   *server,
+                           guint32         id,
+                           const gchar     ***srcs,
+                           const gchar     ***dsts,
+                           GError          **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  g_object_get(G_OBJECT(route), "sources", srcs, "sinks", dsts, NULL);
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_set_endpoint_uri(OctopusServer   *server,
+                                guint32         id,
+                                const gchar     *ep_name,
+                                const gchar     *uri,
+                                GError          **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!octopus_route_set_endpoint_uri(route, ep_name, uri))
+  {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not set URI");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_get_endpoint_uri(OctopusServer   *server,
+                                guint32         id,
+                                const gchar     *ep_name,
+                                const gchar     **uri,
+                                GError          **error)
+{
+  OctopusRoute          *route;
+  const OctopusEndpoint *endpoint;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!(endpoint = octopus_route_get_endpoint(route, ep_name))) {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Unknown endpoint");
+    return FALSE;
+  }
+
+  *uri = endpoint->uri;
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_set_x11_display(OctopusServer   *server,
+                               gulong          id,
+                               gchar           *endpoint,
+                               gchar           *display,
+                               gulong          window,
+                               GError          **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!octopus_route_set_endpoint_x11_display (route, endpoint, display, window)) {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not set x11 display");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_play(OctopusServer  *server,
+                    guint32        id,
+                    GError         **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!octopus_route_play(route)) {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not start playback");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_pause(OctopusServer *server,
+                     guint32       id,
+                     GError        **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!octopus_route_pause(route)) {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not pause playback");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+octopus_server_stop(OctopusServer  *server,
+                    guint32        id,
+                    GError         **error)
+{
+  OctopusRoute *route;
+
+  g_assert(OCTOPUS_IS_SERVER (server));
+
+  if(!(route = get_route_by_id(server, id, error)))
+    return FALSE;
+
+  if(!octopus_route_stop(route)) {
+    g_set_error(error, OCTOPUS_SERVER_ERROR, OCTOPUS_SERVER_ERROR_FAILED, "Could not stop playback");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/*
+ * D-Bus API signals
+ */
+
+static void
+route_playback_error(OctopusRoute *route,
+                     const gchar  *error,
+                     gpointer     user_data)
+{
+  OctopusServer *server = (OctopusServer *)user_data;
+  guint         id;
+
+  g_object_get(G_OBJECT(route), "id", &id, NULL);
+
+  g_signal_emit(server, signals[SIGNAL_PLAYBACK_ERROR], 0, id, error);
+}
+
+static void
+route_stream_position(OctopusRoute *route,
+                      guint        pos,
+                      guint        dur,
+                      gpointer     user_data)
+{
+  OctopusServer *server = (OctopusServer *)user_data;
+  guint         id;
+
+  g_object_get(G_OBJECT(route), "id", &id, NULL);
+
+  g_signal_emit(server, signals[SIGNAL_STREAM_POSITION], 0, id, pos, dur);
+}
+
+static void
+route_state_changed(OctopusRoute      *route,
+                    OctopusRouteState state,
+                    gpointer          user_data)
+{
+  OctopusServer *server = (OctopusServer *)user_data;
+  guint         id;
+
+  g_object_get(G_OBJECT(route), "id", &id, NULL);
+
+  g_signal_emit(server, signals[SIGNAL_STREAM_STATE_CHANGED], 0, id, state);
+}
+
+/* Emacs indentatation information
+   Local Variables:
+   indent-tabs-mode:nil
+   tab-width:2
+   c-set-offset:2
+   c-basic-offset:2
+   End:
+*/
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2
+
diff --git a/src/octopus-server.h b/src/octopus-server.h
new file mode 100644 (file)
index 0000000..7fd07eb
--- /dev/null
@@ -0,0 +1,154 @@
+/*   Octopus Media Engine
+ *   Copyright 2008 Movial Creative Technologies Inc
+ *
+ *   Authors: Mikko Rasa, <mikko.rasa@movial.fi>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef _OCTOPUS_SERVER_H_
+#define _OCTOPUS_SERVER_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib-bindings.h>
+
+G_BEGIN_DECLS
+
+#define DBUS_IXS_OCTOPUS_SERVICE    "com.ixs.OctopusService"
+#define DBUS_IXS_OCTOPUS_PATH       "/com/ixs/OctopusService"
+#define DBUS_IXS_OCTOPUS_INTERFACE  "com.ixs.OctopusService"
+
+#define OCTOPUS_TYPE_SERVER             \
+        (octopus_server_get_type())
+#define OCTOPUS_SERVER(obj)             \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), OCTOPUS_TYPE_SERVER, OctopusServer))
+#define OCTOPUS_IS_SERVER(obj)          \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), OCTOPUS_TYPE_SERVER))
+#define OCTOPUS_SERVER_CLASS(klass)     \
+        (G_TYPE_CHECK_CLASS_CAST((klass), OCTOPUS_TYPE_SERVER, OctopusServerClass))
+#define OCTOPUS_IS_SERVER_CLASS(klass)  \
+        (G_TYPE_CHECK_CLASS_TYPE((klass), OCTOPUS_TYPE_SERVER))
+#define OCTOPUS_SERVER_GET_CLASS(obj)   \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), OCTOPUS_TYPE_SERVER, OctopusServerClass))
+
+typedef struct _OctopusServer        OctopusServer;
+typedef struct _OctopusServerClass   OctopusServerClass;
+typedef struct _OctopusServerPrivate OctopusServerPrivate;
+
+struct _OctopusServer {
+  GObject              parent;
+
+  OctopusServerPrivate *priv;
+};
+
+struct _OctopusServerClass {
+  GObjectClass parent;
+};
+
+/* Public API */
+
+GType
+octopus_server_get_type        (void);
+
+gboolean
+octopus_server_register_service(OctopusServer *server);
+
+/* D-Bus API */
+
+/* Endpoint queries */
+gboolean
+octopus_server_get_endpoints   (OctopusServer   *server,
+                                gchar           ***sources,
+                                gchar           ***dests,
+                                GError          **error);
+gboolean
+octopus_server_get_sources     (OctopusServer   *server,
+                                gchar           ***sources,
+                                GError          **error);
+gboolean
+octopus_server_get_destinations(OctopusServer   *server,
+                                gchar           ***dests,
+                                GError          **error);
+
+/* Route management */
+gboolean
+octopus_server_new             (OctopusServer  *server,
+                                guint32        *id,
+                                GError         **error);
+gboolean
+octopus_server_release         (OctopusServer  *server,
+                                guint32        id,
+                                GError         **error);
+
+/* Route configuration */
+gboolean
+octopus_server_set_mapping     (OctopusServer  *server,
+                                guint32        id,
+                                const gchar    **srcs,
+                                const gchar    **dsts,
+                                GError         **error);
+gboolean
+octopus_server_get_mapping     (OctopusServer  *server,
+                                guint32        id,
+                                const gchar    ***srcs,
+                                const gchar    ***dsts,
+                                GError         **error);
+gboolean
+octopus_server_set_endpoint_uri(OctopusServer   *server,
+                                guint32         id,
+                                const gchar     *ep_name,
+                                const gchar     *uri,
+                                GError          **error);
+gboolean
+octopus_server_get_endpoint_uri(OctopusServer   *server,
+                                guint32         id,
+                                const gchar     *ep_name,
+                                const gchar     **uri,
+                                GError          **error);
+gboolean
+octopus_server_set_x11_display (OctopusServer   *server,
+                                gulong          id,
+                                gchar           *endpoint,
+                                gchar           *display,
+                                gulong          window,
+                                GError          **error);
+
+/* Playback control */
+gboolean
+octopus_server_play            (OctopusServer  *server,
+                                guint32        id,
+                                GError         **error);
+gboolean
+octopus_server_pause           (OctopusServer  *server,
+                                guint32        id,
+                                GError         **error);
+gboolean
+octopus_server_stop            (OctopusServer  *server,
+                                guint32        id,
+                                GError         **error);
+
+G_END_DECLS
+
+#endif
+
+/* Emacs indentatation information
+   Local Variables:
+   indent-tabs-mode:nil
+   tab-width:2
+   c-set-offset:2
+   c-basic-offset:2
+   End:
+*/
+// vim: filetype=c:expandtab:shiftwidth=2:tabstop=2:softtabstop=2