Mercurial > pidgin
changeset 23807:ff29208e03ef
propagate from branch 'im.pidgin.pidgin' (head 75f0460d3679231b61d572e76fe02534a597c6b5)
to branch 'im.pidgin.pidgin.vv' (head 7872d32a910cd4c1fbc3e2457295e4c1d40188f5)
line wrap: on
line diff
--- a/configure.ac Tue May 27 04:11:04 2008 +0000 +++ b/configure.ac Tue May 27 06:27:46 2008 +0000 @@ -47,7 +47,7 @@ m4_define([purple_major_version], [2]) m4_define([purple_minor_version], [5]) m4_define([purple_micro_version], [0]) -m4_define([purple_version_suffix], [devel]) +m4_define([purple_version_suffix], [vv-devel]) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix])) @@ -726,6 +726,46 @@ fi dnl ####################################################################### +dnl # Check for Farsight +dnl ####################################################################### +AC_ARG_ENABLE(farsight, + [AC_HELP_STRING([--disable-vv], [compile without voice and video support])], + enable_farsight="$enableval", enable_farsight="yes") +if test "x$enable_farsight" != "xno"; then + PKG_CHECK_MODULES(FARSIGHT, [farsight-0.1], [ + AC_DEFINE(USE_FARSIGHT, 1, [Use Farsight for voice and video]) + AC_SUBST(FARSIGHT_CFLAGS) + AC_SUBST(FARSIGHT_LIBS) + ], [ + AC_MSG_RESULT(no) + enable_farsight="no" + ]) +fi + + +dnl ####################################################################### +dnl # Check for GStreamer-properties +dnl ####################################################################### +AC_ARG_ENABLE(gstprops, + [AC_HELP_STRING([--disable-gstprops], [compile without gstreamer props])], + enable_gstprops="$enableval", enable_gstprops="yes") +if test "x$enable_gstprops" != "xno"; +then + dnl gstreamer-libs-$GST_MAJORMINOR + dnl gstreamer-gconf-$GST_MAJORMINOR + PKG_CHECK_MODULES(GSTPROPS, [gstreamer-0.10 gstreamer-plugins-base-0.10 libxml-2.0], [ + GSTPROPS_LIBS="$GSTPROPS_LIBS -lgstinterfaces-0.10" + AC_DEFINE(USE_GSTPROPS, 1, [Use GStreamer property probe for finding devices]) + AC_SUBST(GSTPROPS_LIBS) + AC_SUBST(GSTPROPS_CFLAGS) + ], [ + AC_MSG_RESULT(no) + enable_gstprops="no" + ]) +fi + + +dnl ####################################################################### dnl # Check for Meanwhile headers (for Sametime) dnl ####################################################################### AC_ARG_ENABLE(meanwhile, @@ -2403,6 +2443,7 @@ echo Protocols to link statically.. : $STATIC_PRPLS echo echo Build with GStreamer support.. : $enable_gst +echo Build with voice and video.... : $enable_farsight echo Build with D-Bus support...... : $enable_dbus if test "x$enable_dbus" = "xyes" ; then eval eval echo D-Bus services directory...... : $DBUS_SERVICES_DIR
--- a/finch/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/finch/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -26,6 +26,7 @@ finch.c \ gntidle.c \ gntlog.c \ + gntmedia.c \ gntnotify.c \ gntplugin.c \ gntpounce.c \ @@ -47,6 +48,7 @@ finch.h \ gntidle.h \ gntlog.h \ + gntmedia.h \ gntnotify.h \ gntplugin.h \ gntpounce.h \ @@ -69,6 +71,7 @@ $(INTLLIBS) \ $(GLIB_LIBS) \ $(LIBXML_LIBS) \ + $(FARSIGHT_LIBS) \ $(GNT_LIBS) \ $(GSTREAMER_LIBS) \ ./libgnt/libgnt.la \ @@ -88,5 +91,6 @@ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GSTREAMER_CFLAGS) \ $(GNT_CFLAGS)
--- a/finch/gntaccount.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntaccount.c Tue May 27 06:27:46 2008 +0000 @@ -1057,3 +1057,4 @@ return &ui_ops; } +
--- a/finch/gntdebug.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntdebug.c Tue May 27 06:27:46 2008 +0000 @@ -23,6 +23,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "util.h" + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -36,7 +38,6 @@ #include "gntdebug.h" #include "finch.h" #include "notify.h" -#include "util.h" #include <stdio.h> #include <string.h> @@ -347,6 +348,11 @@ #ifdef USE_GSTREAMER REGISTER_G_LOG_HANDLER("GStreamer"); #endif +#ifdef USE_FARSIGHT + REGISTER_G_LOG_HANDLER("farsight"); + REGISTER_G_LOG_HANDLER("farsight-transmitter"); + REGISTER_G_LOG_HANDLER("farsight-rtp"); +#endif g_set_print_handler(print_stderr); /* Redirect the debug messages to stderr */ if (!purple_debug_is_enabled())
--- a/finch/gntft.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntft.c Tue May 27 06:27:46 2008 +0000 @@ -25,6 +25,12 @@ */ #include "finch.h" +#include "debug.h" +#include "notify.h" +#include "ft.h" +#include "prpl.h" +#include "util.h" + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -32,12 +38,6 @@ #include <gntlabel.h> #include <gnttree.h> -#include "debug.h" -#include "notify.h" -#include "ft.h" -#include "prpl.h" -#include "util.h" - #include "gntft.h" #include "prefs.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/gntmedia.c Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,460 @@ +/** + * @file gntmedia.c GNT Media API + * @ingroup finch + */ + +/* finch + * + * Finch is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "finch.h" +#include "mediamanager.h" + +#include "gntconv.h" +#include "gntmedia.h" + +#include "gnt.h" +#include "gntbutton.h" +#include "gntbox.h" +#include "gntlabel.h" + +#include "cmds.h" +#include "conversation.h" +#include "debug.h" + +/* An incredibly large part of the following is from gtkmedia.c */ +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> + +#undef hangup + +struct _FinchMediaPrivate +{ + PurpleMedia *media; + GstElement *send_level; + GstElement *recv_level; + + GntWidget *accept; + GntWidget *reject; + GntWidget *hangup; + GntWidget *calling; + + PurpleConversation *conv; +}; + +#define FINCH_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), FINCH_TYPE_MEDIA, FinchMediaPrivate)) + +static void finch_media_class_init (FinchMediaClass *klass); +static void finch_media_init (FinchMedia *media); +static void finch_media_finalize (GObject *object); +static void finch_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void finch_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); + +static GntBoxClass *parent_class = NULL; + +enum { + MESSAGE, + LAST_SIGNAL +}; +static guint finch_media_signals[LAST_SIGNAL] = {0}; + +enum { + PROP_0, + PROP_MEDIA, + PROP_SEND_LEVEL, + PROP_RECV_LEVEL +}; + +GType +finch_media_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(FinchMediaClass), + NULL, + NULL, + (GClassInitFunc) finch_media_class_init, + NULL, + NULL, + sizeof(FinchMedia), + 0, + (GInstanceInitFunc) finch_media_init, + NULL + }; + type = g_type_register_static(GNT_TYPE_BOX, "FinchMedia", &info, 0); + } + return type; +} + + +static void +finch_media_class_init (FinchMediaClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = finch_media_finalize; + gobject_class->set_property = finch_media_set_property; + gobject_class->get_property = finch_media_get_property; + + g_object_class_install_property(gobject_class, PROP_MEDIA, + g_param_spec_object("media", + "PurpleMedia", + "The PurpleMedia associated with this media.", + PURPLE_TYPE_MEDIA, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, PROP_SEND_LEVEL, + g_param_spec_object("send-level", + "Send level", + "The GstElement of this media's send 'level'", + GST_TYPE_ELEMENT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, PROP_RECV_LEVEL, + g_param_spec_object("recv-level", + "Receive level", + "The GstElement of this media's recv 'level'", + GST_TYPE_ELEMENT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + finch_media_signals[MESSAGE] = g_signal_new("message", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + g_type_class_add_private(klass, sizeof(FinchMediaPrivate)); +} + + +static void +finch_media_init (FinchMedia *media) +{ + media->priv = FINCH_MEDIA_GET_PRIVATE(media); + + media->priv->calling = gnt_label_new(_("Calling ... ")); + media->priv->hangup = gnt_button_new(_("Hangup")); + media->priv->accept = gnt_button_new(_("Accept")); + media->priv->reject = gnt_button_new(_("Reject")); + + gnt_box_set_alignment(GNT_BOX(media), GNT_ALIGN_MID); + + gnt_box_add_widget(GNT_BOX(media), media->priv->accept); + gnt_box_add_widget(GNT_BOX(media), media->priv->reject); +} + +static void +finch_media_finalize (GObject *media) +{ +} + +static void +finch_media_emit_message(FinchMedia *gntmedia, const char *msg) +{ + g_signal_emit(gntmedia, finch_media_signals[MESSAGE], 0, msg); +} + +static gboolean +level_message_cb(GstBus *bus, GstMessage *message, FinchMedia *gntmedia) +{ + /* XXX: I am hesitant to just remove this function altogether, because I don't + * know how necessary it is to have a callback to 'message'. If it isn't essential, + * I suppose this should be removed. + */ + return TRUE; +#if 0 + const GstStructure *s; + const gchar *name; + + int channels; + gdouble rms_db, peak_db, decay_db; + gdouble rms; + const GValue *list; + const GValue *value; + + GstElement *src = GST_ELEMENT(message); + + if (message->type != GST_MESSAGE_ELEMENT) + return TRUE; + + s = gst_message_get_structure(message); + name = gst_structure_get_name(s); + + if (strcmp(name, "level")) + return TRUE; + + list = gst_structure_get_value(s, "rms"); + + /* Only bother with the first channel. */ + value = gst_value_list_get_value(list, 0); + rms_db = g_value_get_double(value); + + if (!strcmp(gst_element_get_name(src), "sendlevel")) + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gntmedia->priv->send_progress), pow(10, rms_db / 20) * 5); + else + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gntmedia->priv->recv_progress), pow(10, rms_db / 20) * 5); + + return TRUE; +#endif +} + +static void +finch_media_ready_cb(PurpleMedia *media, FinchMedia *gntmedia) +{ + GstElement *element = purple_media_get_audio_pipeline(media); + gst_bus_add_signal_watch(GST_BUS(gst_pipeline_get_bus(GST_PIPELINE(element)))); + g_signal_connect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), "message", G_CALLBACK(level_message_cb), gntmedia); +} + +static void +finch_media_accept_cb(PurpleMedia *media, FinchMedia *gntmedia) +{ + GntWidget *parent; + + finch_media_emit_message(gntmedia, _("Call in progress.")); + + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->accept); + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->reject); + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->hangup); + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->calling); + + gnt_box_add_widget(GNT_BOX(gntmedia), gntmedia->priv->hangup); + + gnt_widget_destroy(gntmedia->priv->accept); + gnt_widget_destroy(gntmedia->priv->reject); + gnt_widget_destroy(gntmedia->priv->calling); + gntmedia->priv->accept = NULL; + gntmedia->priv->reject = NULL; + gntmedia->priv->calling = NULL; + + parent = GNT_WIDGET(gntmedia); + while (parent->parent) + parent = parent->parent; + gnt_box_readjust(GNT_BOX(parent)); + gnt_widget_draw(parent); +} + +static void +finch_media_wait_cb(PurpleMedia *media, FinchMedia *gntmedia) +{ + GntWidget *parent; + + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->accept); + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->reject); + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->hangup); + gnt_box_remove(GNT_BOX(gntmedia), gntmedia->priv->calling); + + gnt_box_add_widget(GNT_BOX(gntmedia), gntmedia->priv->calling); + gnt_box_add_widget(GNT_BOX(gntmedia), gntmedia->priv->hangup); + + gnt_widget_destroy(gntmedia->priv->accept); + gnt_widget_destroy(gntmedia->priv->reject); + gntmedia->priv->accept = NULL; + gntmedia->priv->reject = NULL; + + parent = GNT_WIDGET(gntmedia); + while (parent->parent) + parent = parent->parent; + gnt_box_readjust(GNT_BOX(parent)); + gnt_widget_draw(parent); +} + +static void +finch_media_hangup_cb(PurpleMedia *media, FinchMedia *gntmedia) +{ + finch_media_emit_message(gntmedia, _("You have ended the call.")); + finch_conversation_set_info_widget(gntmedia->priv->conv, NULL); + gnt_widget_destroy(GNT_WIDGET(gntmedia)); +} + +static void +finch_media_got_hangup_cb(PurpleMedia *media, FinchMedia *gntmedia) +{ + finch_media_emit_message(gntmedia, _("The call has been terminated.")); + finch_conversation_set_info_widget(gntmedia->priv->conv, NULL); + gnt_widget_destroy(GNT_WIDGET(gntmedia)); +} + +static void +finch_media_reject_cb(PurpleMedia *media, FinchMedia *gntmedia) +{ + finch_media_emit_message(gntmedia, _("You have rejected the call.")); + finch_conversation_set_info_widget(gntmedia->priv->conv, NULL); + gnt_widget_destroy(GNT_WIDGET(gntmedia)); +} + +static void +finch_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FinchMedia *media; + g_return_if_fail(FINCH_IS_MEDIA(object)); + + media = FINCH_MEDIA(object); + switch (prop_id) { + case PROP_MEDIA: + if (media->priv->media) + g_object_unref(media->priv->media); + media->priv->media = g_value_get_object(value); + g_object_ref(media->priv->media); + g_signal_connect_swapped(G_OBJECT(media->priv->accept), "activate", + G_CALLBACK(purple_media_accept), media->priv->media); + g_signal_connect_swapped(G_OBJECT(media->priv->reject), "activate", + G_CALLBACK(purple_media_reject), media->priv->media); + g_signal_connect_swapped(G_OBJECT(media->priv->hangup), "activate", + G_CALLBACK(purple_media_hangup), media->priv->media); + + g_signal_connect(G_OBJECT(media->priv->media), "accepted", + G_CALLBACK(finch_media_accept_cb), media); + g_signal_connect(G_OBJECT(media->priv->media) ,"ready", + G_CALLBACK(finch_media_ready_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "wait", + G_CALLBACK(finch_media_wait_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "hangup", + G_CALLBACK(finch_media_hangup_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "reject", + G_CALLBACK(finch_media_reject_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "got-hangup", + G_CALLBACK(finch_media_got_hangup_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "got-accept", + G_CALLBACK(finch_media_accept_cb), media); + break; + case PROP_SEND_LEVEL: + if (media->priv->send_level) + gst_object_unref(media->priv->send_level); + media->priv->send_level = g_value_get_object(value); + g_object_ref(media->priv->send_level); + break; + case PROP_RECV_LEVEL: + if (media->priv->recv_level) + gst_object_unref(media->priv->recv_level); + media->priv->recv_level = g_value_get_object(value); + g_object_ref(media->priv->recv_level); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finch_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FinchMedia *media; + g_return_if_fail(FINCH_IS_MEDIA(object)); + + media = FINCH_MEDIA(object); + + switch (prop_id) { + case PROP_MEDIA: + g_value_set_object(value, media->priv->media); + break; + case PROP_SEND_LEVEL: + g_value_set_object(value, media->priv->send_level); + break; + case PROP_RECV_LEVEL: + g_value_set_object(value, media->priv->recv_level); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +GntWidget * +finch_media_new(PurpleMedia *media, GstElement *sendlevel, GstElement *recvlevel) +{ + return GNT_WIDGET(g_object_new(finch_media_get_type(), + "media", media, + "send-level", sendlevel, + "recv-level", recvlevel, + "vertical", FALSE, + "homogeneous", FALSE, + NULL)); +} + +#endif /* USE_FARSIGHT */ + +static void +gntmedia_message_cb(FinchMedia *gntmedia, const char *msg, PurpleConversation *conv) +{ + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); + } +} + +static void +finch_new_media(PurpleMediaManager *manager, PurpleMedia *media, gpointer null) +{ + GstElement *sendbin, *sendlevel; + GstElement *recvbin, *recvlevel; + GntWidget *gntmedia; + PurpleConversation *conv; + + purple_media_audio_init_src(&sendbin, &sendlevel); + purple_media_audio_init_recv(&recvbin, &recvlevel); + + purple_media_set_audio_src(media, sendbin); + purple_media_set_audio_sink(media, recvbin); + + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, + purple_connection_get_account(purple_media_get_connection(media)), + purple_media_get_screenname(media)); + + gntmedia = finch_media_new(media, sendlevel, recvlevel); + g_signal_connect(G_OBJECT(gntmedia), "message", G_CALLBACK(gntmedia_message_cb), conv); + FINCH_MEDIA(gntmedia)->priv->conv = conv; + finch_conversation_set_info_widget(conv, gntmedia); +} + +static PurpleCmdRet +call_cmd_cb(PurpleConversation *conv, const char *cmd, char **args, + char **eror, gpointer data) +{ + PurpleConnection *gc = purple_conversation_get_gc(conv); + + PurpleMedia *media = + serv_initiate_media(gc, + purple_conversation_get_name(conv), + PURPLE_MEDIA_RECV_AUDIO & PURPLE_MEDIA_SEND_AUDIO); + + if (!media) + return PURPLE_CMD_STATUS_FAILED; + + purple_media_wait(media); + return PURPLE_CMD_STATUS_OK; +} + +void finch_media_manager_init(void) +{ + PurpleMediaManager *manager = purple_media_manager_get(); + g_signal_connect(G_OBJECT(manager), "init-media", G_CALLBACK(finch_new_media), NULL); + purple_cmd_register("call", "", PURPLE_CMD_P_DEFAULT, + PURPLE_CMD_FLAG_IM, NULL, + call_cmd_cb, _("call: Make an audio call."), NULL); +} + +void finch_media_manager_uninit(void) +{ + PurpleMediaManager *manager = purple_media_manager_get(); + g_signal_handlers_disconnect_by_func(G_OBJECT(manager), + G_CALLBACK(finch_new_media), NULL); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/gntmedia.h Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,78 @@ +/** + * @file gntmedia.h GNT Media API + * @ingroup finch + */ + +/* finch + * + * Finch is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef GNT_MEDIA_H +#define GNT_MEDIA_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> +#include <glib-object.h> +#include "gntbox.h" + +G_BEGIN_DECLS + +#define FINCH_TYPE_MEDIA (finch_media_get_type()) +#define FINCH_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FINCH_TYPE_MEDIA, FinchMedia)) +#define FINCH_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FINCH_TYPE_MEDIA, FinchMediaClass)) +#define FINCH_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FINCH_TYPE_MEDIA)) +#define FINCH_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FINCH_TYPE_MEDIA)) +#define FINCH_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FINCH_TYPE_MEDIA, FinchMediaClass)) + +typedef struct _FinchMedia FinchMedia; +typedef struct _FinchMediaClass FinchMediaClass; +typedef struct _FinchMediaPrivate FinchMediaPrivate; +typedef enum _FinchMediaState FinchMediaState; + +struct _FinchMediaClass +{ + GntBoxClass parent_class; +}; + +struct _FinchMedia +{ + GntBox parent; + FinchMediaPrivate *priv; +}; + +GType finch_media_get_type(void); + +GntWidget *finch_media_new(PurpleMedia *media, GstElement *send_level, GstElement *recv_level); + +void finch_media_manager_init(void); + +void finch_media_manager_uninit(void); + +G_END_DECLS + +#endif /* USE_FARSIGHT */ + +#endif /* GNT_MEDIA_H */ +
--- a/finch/gntnotify.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntnotify.c Tue May 27 06:27:46 2008 +0000 @@ -23,6 +23,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include <util.h> + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -33,7 +35,6 @@ #include "finch.h" -#include <util.h> #include "gntnotify.h"
--- a/finch/gntplugin.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntplugin.c Tue May 27 06:27:46 2008 +0000 @@ -23,6 +23,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "notify.h" +#include "request.h" + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -34,8 +37,6 @@ #include "finch.h" #include "debug.h" -#include "notify.h" -#include "request.h" #include "gntplugin.h" #include "gntrequest.h"
--- a/finch/gntpounce.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntpounce.c Tue May 27 06:27:46 2008 +0000 @@ -24,6 +24,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * */ +#include "internal.h" +#include "account.h" +#include "conversation.h" +#include "debug.h" +#include "notify.h" +#include "prpl.h" +#include "request.h" +#include "server.h" +#include "util.h" + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -37,15 +47,6 @@ #include "finch.h" -#include "account.h" -#include "conversation.h" -#include "debug.h" -#include "notify.h" -#include "prpl.h" -#include "request.h" -#include "server.h" -#include "util.h" - #include "gntpounce.h"
--- a/finch/gntrequest.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntrequest.c Tue May 27 06:27:46 2008 +0000 @@ -23,6 +23,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "util.h" + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -37,7 +40,6 @@ #include "finch.h" #include "gntrequest.h" #include "debug.h" -#include "util.h" typedef struct {
--- a/finch/gntstatus.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntstatus.c Tue May 27 06:27:46 2008 +0000 @@ -23,6 +23,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include <notify.h> +#include <request.h> + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> @@ -35,9 +39,6 @@ #include "finch.h" -#include <notify.h> -#include <request.h> - #include "gntstatus.h" static struct
--- a/finch/gntui.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/gntui.c Tue May 27 06:27:46 2008 +0000 @@ -19,9 +19,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include <prefs.h> #include "finch.h" -#include "gntui.h" #include "gntaccount.h" #include "gntblist.h" @@ -31,6 +31,7 @@ #include "gntdebug.h" #include "gntft.h" #include "gntlog.h" +#include "gntmedia.h" #include "gntnotify.h" #include "gntplugin.h" #include "gntpounce.h" @@ -40,7 +41,7 @@ #include "gntstatus.h" #include "gntsound.h" -#include <prefs.h> +#include "gntui.h" void gnt_ui_init() { @@ -91,6 +92,9 @@ finch_roomlist_init(); purple_roomlist_set_ui_ops(finch_roomlist_get_ui_ops()); + /* Media */ + finch_media_manager_init(); + gnt_register_action(_("Accounts"), finch_accounts_show_all); gnt_register_action(_("Buddy List"), finch_blist_show); gnt_register_action(_("Buddy Pounces"), finch_pounces_manager_show); @@ -136,6 +140,8 @@ finch_roomlist_uninit(); purple_roomlist_set_ui_ops(NULL); + finch_media_manager_uninit(); + gnt_quit(); #endif }
--- a/finch/libgnt/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/finch/libgnt/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -89,6 +89,7 @@ AM_CPPFLAGS = \ $(GLIB_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GNT_CFLAGS) \ $(DEBUG_CFLAGS) \ $(LIBXML_CFLAGS) \
--- a/finch/libgnt/gntkeys.h Tue May 27 04:11:04 2008 +0000 +++ b/finch/libgnt/gntkeys.h Tue May 27 06:27:46 2008 +0000 @@ -165,5 +165,6 @@ #undef lines #undef buttons #undef newline +#undef set_clock #endif
--- a/finch/libgnt/wms/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/finch/libgnt/wms/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -34,5 +34,6 @@ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(GNT_CFLAGS) \ - $(PLUGIN_CFLAGS) + $(PLUGIN_CFLAGS) \ + $(FARSIGHT_CFLAGS)
--- a/finch/libgnt/wms/s.c Tue May 27 04:11:04 2008 +0000 +++ b/finch/libgnt/wms/s.c Tue May 27 06:27:46 2008 +0000 @@ -2,6 +2,7 @@ #include <sys/types.h> #include "internal.h" +#include "blist.h" #include "gnt.h" #include "gntbox.h" @@ -11,7 +12,6 @@ #include "gntwindow.h" #include "gntlabel.h" -#include "blist.h" #define TYPE_S (s_get_gtype())
--- a/finch/plugins/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/finch/plugins/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -42,6 +42,7 @@ -I$(top_srcdir)/finch \ -I$(top_srcdir)/finch/libgnt \ $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GLIB_CFLAGS) \ $(GNT_CFLAGS) \ $(PLUGIN_CFLAGS)
--- a/libpurple/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -51,6 +51,8 @@ idle.c \ imgstore.c \ log.c \ + media.c \ + mediamanager.c \ mime.c \ nat-pmp.c \ network.c \ @@ -104,6 +106,8 @@ idle.h \ imgstore.h \ log.h \ + media.h \ + mediamanager.h \ mime.h \ nat-pmp.h \ network.h \ @@ -248,6 +252,9 @@ $(LIBXML_LIBS) \ $(NETWORKMANAGER_LIBS) \ $(INTLLIBS) \ + $(FARSIGHT_LIBS) \ + $(GSTREAMER_LIBS) \ + $(GSTPROPS_LIBS) \ -lm AM_CPPFLAGS = \ @@ -260,4 +267,7 @@ $(DEBUG_CFLAGS) \ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(GSTPROPS_CFLAGS) \ $(NETWORKMANAGER_CFLAGS)
--- a/libpurple/example/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/example/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -8,6 +8,7 @@ $(INTLLIBS) \ $(GLIB_LIBS) \ $(LIBXML_LIBS) \ + $(FARSIGHT_LIBS) \ $(top_builddir)/libpurple/libpurple.la AM_CPPFLAGS = \ @@ -23,4 +24,5 @@ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(LIBXML_CFLAGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media.c Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,635 @@ +/** + * @file media.c Media API + * @ingroup core + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 "internal.h" + +#include "connection.h" +#include "media.h" + +#include "debug.h" + +#ifdef USE_FARSIGHT +#ifdef USE_GSTPROPS + +#include <gst/interfaces/propertyprobe.h> +#include <farsight/farsight.h> + +struct _PurpleMediaPrivate +{ + FarsightSession *farsight_session; + + char *name; + PurpleConnection *connection; + GstElement *audio_src; + GstElement *audio_sink; + GstElement *video_src; + GstElement *video_sink; + + FarsightStream *audio_stream; + FarsightStream *video_stream; +}; + +#define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate)) + +static void purple_media_class_init (PurpleMediaClass *klass); +static void purple_media_init (PurpleMedia *media); +static void purple_media_finalize (GObject *object); +static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); + +static GObjectClass *parent_class = NULL; + + + +enum { + READY, + WAIT, + ACCEPTED, + HANGUP, + REJECT, + GOT_HANGUP, + GOT_ACCEPT, + LAST_SIGNAL +}; +static guint purple_media_signals[LAST_SIGNAL] = {0}; + +enum { + PROP_0, + PROP_FARSIGHT_SESSION, + PROP_NAME, + PROP_CONNECTION, + PROP_AUDIO_SRC, + PROP_AUDIO_SINK, + PROP_VIDEO_SRC, + PROP_VIDEO_SINK, + PROP_VIDEO_STREAM, + PROP_AUDIO_STREAM +}; + +GType +purple_media_get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(PurpleMediaClass), + NULL, + NULL, + (GClassInitFunc) purple_media_class_init, + NULL, + NULL, + sizeof(PurpleMedia), + 0, + (GInstanceInitFunc) purple_media_init, + NULL + }; + type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0); + } + return type; +} + + +static void +purple_media_class_init (PurpleMediaClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = purple_media_finalize; + gobject_class->set_property = purple_media_set_property; + gobject_class->get_property = purple_media_get_property; + + g_object_class_install_property(gobject_class, PROP_FARSIGHT_SESSION, + g_param_spec_object("farsight-session", + "Farsight session", + "The FarsightSession associated with this media.", + FARSIGHT_TYPE_SESSION, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_NAME, + g_param_spec_string("screenname", + "Screenname", + "The screenname of the remote user", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_CONNECTION, + g_param_spec_pointer("connection", + "Connection", + "The PurpleConnection associated with this session", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_AUDIO_SRC, + g_param_spec_object("audio-src", + "Audio source", + "The GstElement used to source audio", + GST_TYPE_ELEMENT, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_AUDIO_SINK, + g_param_spec_object("audio-sink", + "Audio sink", + "The GstElement used to sink audio", + GST_TYPE_ELEMENT, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_VIDEO_SRC, + g_param_spec_object("video-src", + "Video source", + "The GstElement used to source video", + GST_TYPE_ELEMENT, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_VIDEO_SINK, + g_param_spec_object("video-sink", + "Audio source", + "The GstElement used to sink video", + GST_TYPE_ELEMENT, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_VIDEO_STREAM, + g_param_spec_object("video-stream", + "Video stream", + "The FarsightStream used for video", + FARSIGHT_TYPE_STREAM, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_AUDIO_STREAM, + g_param_spec_object("audio-stream", + "Audio stream", + "The FarsightStream used for audio", + FARSIGHT_TYPE_STREAM, + G_PARAM_READWRITE)); + + purple_media_signals[READY] = g_signal_new("ready", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[WAIT] = g_signal_new("wait", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[ACCEPTED] = g_signal_new("accepted", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[HANGUP] = g_signal_new("hangup", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[REJECT] = g_signal_new("reject", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[GOT_HANGUP] = g_signal_new("got-hangup", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[GOT_ACCEPT] = g_signal_new("got-accept", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private(klass, sizeof(PurpleMediaPrivate)); +} + + +static void +purple_media_init (PurpleMedia *media) +{ + media->priv = PURPLE_MEDIA_GET_PRIVATE(media); + memset(media->priv, 0, sizeof(media->priv)); +} + +static void +purple_media_finalize (GObject *media) +{ + parent_class->finalize(media); +} + +static void +purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + PurpleMedia *media; + g_return_if_fail(PURPLE_IS_MEDIA(object)); + + media = PURPLE_MEDIA(object); + + switch (prop_id) { + case PROP_FARSIGHT_SESSION: + if (media->priv->farsight_session) + g_object_unref(media->priv->farsight_session); + media->priv->farsight_session = g_value_get_object(value); + g_object_ref(media->priv->farsight_session); + break; + case PROP_NAME: + g_free(media->priv->name); + media->priv->name = g_value_dup_string(value); + break; + case PROP_CONNECTION: + media->priv->connection = g_value_get_pointer(value); + break; + case PROP_AUDIO_SRC: + if (media->priv->audio_src) + gst_object_unref(media->priv->audio_src); + media->priv->audio_src = g_value_get_object(value); + gst_object_ref(media->priv->audio_src); + break; + case PROP_AUDIO_SINK: + if (media->priv->audio_sink) + gst_object_unref(media->priv->audio_sink); + media->priv->audio_sink = g_value_get_object(value); + gst_object_ref(media->priv->audio_sink); + break; + case PROP_VIDEO_SRC: + if (media->priv->video_src) + gst_object_unref(media->priv->video_src); + media->priv->video_src = g_value_get_object(value); + gst_object_ref(media->priv->video_src); + break; + case PROP_VIDEO_SINK: + if (media->priv->video_sink) + gst_object_unref(media->priv->video_sink); + media->priv->video_sink = g_value_get_object(value); + gst_object_ref(media->priv->video_sink); + break; + case PROP_VIDEO_STREAM: + if (media->priv->video_stream) + g_object_unref(media->priv->video_stream); + media->priv->video_stream = g_value_get_object(value); + gst_object_ref(media->priv->video_stream); + break; + case PROP_AUDIO_STREAM: + if (media->priv->audio_stream) + g_object_unref(media->priv->audio_stream); + media->priv->audio_stream = g_value_get_object(value); + gst_object_ref(media->priv->audio_stream); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + PurpleMedia *media; + g_return_if_fail(PURPLE_IS_MEDIA(object)); + + media = PURPLE_MEDIA(object); + + switch (prop_id) { + case PROP_FARSIGHT_SESSION: + g_value_set_object(value, media->priv->farsight_session); + break; + case PROP_NAME: + g_value_set_string(value, media->priv->name); + break; + case PROP_CONNECTION: + g_value_set_pointer(value, media->priv->connection); + break; + case PROP_AUDIO_SRC: + g_value_set_object(value, media->priv->audio_src); + break; + case PROP_AUDIO_SINK: + g_value_set_object(value, media->priv->audio_sink); + break; + case PROP_VIDEO_SRC: + g_value_set_object(value, media->priv->video_src); + break; + case PROP_VIDEO_SINK: + g_value_set_object(value, media->priv->video_sink); + break; + case PROP_VIDEO_STREAM: + g_value_set_object(value, media->priv->video_stream); + break; + case PROP_AUDIO_STREAM: + g_value_set_object(value, media->priv->audio_stream); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + +} + +void +purple_media_get_elements(PurpleMedia *media, GstElement **audio_src, GstElement **audio_sink, + GstElement **video_src, GstElement **video_sink) +{ + if (audio_src) + g_object_get(G_OBJECT(media), "audio-src", *audio_src, NULL); + if (audio_sink) + g_object_get(G_OBJECT(media), "audio-sink", *audio_sink, NULL); + if (video_src) + g_object_get(G_OBJECT(media), "video-src", *video_src, NULL); + if (video_sink) + g_object_get(G_OBJECT(media), "video-sink", *video_sink, NULL); + +} + +void +purple_media_set_audio_src(PurpleMedia *media, GstElement *audio_src) +{ + g_object_set(G_OBJECT(media), "audio-src", audio_src, NULL); +} + +void +purple_media_set_audio_sink(PurpleMedia *media, GstElement *audio_sink) +{ + g_object_set(G_OBJECT(media), "audio-sink", audio_sink, NULL); +} + +void +purple_media_set_video_src(PurpleMedia *media, GstElement *video_src) +{ + g_object_set(G_OBJECT(media), "video-src", video_src, NULL); +} + +void +purple_media_set_video_sink(PurpleMedia *media, GstElement *video_sink) +{ + g_object_set(G_OBJECT(media), "video-sink", video_sink, NULL); +} + +GstElement * +purple_media_get_audio_src(PurpleMedia *media) +{ + GstElement *ret; + g_object_get(G_OBJECT(media), "audio-src", &ret, NULL); + return ret; +} + +GstElement * +purple_media_get_audio_sink(PurpleMedia *media) +{ + GstElement *ret; + g_object_get(G_OBJECT(media), "audio-sink", &ret, NULL); + return ret; +} + +GstElement * +purple_media_get_video_src(PurpleMedia *media) +{ + GstElement *ret; + g_object_get(G_OBJECT(media), "video-src", &ret, NULL); + return ret; +} + +GstElement * +purple_media_get_video_sink(PurpleMedia *media) +{ + GstElement *ret; + g_object_get(G_OBJECT(media), "video-sink", &ret, NULL); + return ret; +} + +GstElement * +purple_media_get_audio_pipeline(PurpleMedia *media) +{ + FarsightStream *stream; + g_object_get(G_OBJECT(media), "audio-stream", &stream, NULL); +printf("stream: %d\n\n\n", stream); +GstElement *l = farsight_stream_get_pipeline(stream); +printf("Element: %d\n", l); + return farsight_stream_get_pipeline(stream); +} + +PurpleConnection * +purple_media_get_connection(PurpleMedia *media) +{ + PurpleConnection *gc; + g_object_get(G_OBJECT(media), "connection", &gc, NULL); + return gc; +} + +const char * +purple_media_get_screenname(PurpleMedia *media) +{ + const char *ret; + g_object_get(G_OBJECT(media), "screenname", &ret, NULL); + return ret; +} + +void +purple_media_ready(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[READY], 0); +} + +void +purple_media_wait(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[WAIT], 0); +} + +void +purple_media_accept(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[ACCEPTED], 0); +} + +void +purple_media_hangup(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[HANGUP], 0); +} + +void +purple_media_reject(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[REJECT], 0); +} + +void +purple_media_got_hangup(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[GOT_HANGUP], 0); +} + +void +purple_media_got_accept(PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[GOT_ACCEPT], 0); +} + +gchar* +purple_media_get_device_name(GstElement *element, GValue *device) +{ + gchar *name; + + GstElementFactory *factory = gst_element_get_factory(element); + GstElement *temp = gst_element_factory_create(factory, "tmp_src"); + + g_object_set_property (G_OBJECT (temp), "device", device); + g_object_get (G_OBJECT (temp), "device-name", &name, NULL); + gst_object_unref(temp); + + return name; +} + +GList* +purple_media_get_devices(GstElement *element) +{ + GObjectClass *klass; + GstPropertyProbe *probe; + const GParamSpec *pspec; + + const gchar *longname = NULL; + + GstElementFactory *factory = + gst_element_get_factory(element); + + GList *ret = NULL; + + longname = gst_element_factory_get_longname(factory); + klass = G_OBJECT_GET_CLASS(element); + + if (!g_object_class_find_property (klass, "device") || + !GST_IS_PROPERTY_PROBE (element) || + !(probe = GST_PROPERTY_PROBE (element)) || + !(pspec = gst_property_probe_get_property (probe, "device"))) { + purple_debug_info("Found source '%s' (%s) - no device", + longname, GST_PLUGIN_FEATURE (factory)->name); + } else { + gint n; + gchar *name; + GValueArray *array; + + purple_debug_info("media", "Found devices\n"); + + /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */ + if (g_object_class_find_property (klass, "autoprobe")) { + g_object_set (G_OBJECT (element), "autoprobe", FALSE, NULL); + if (g_object_class_find_property (klass, "autoprobe-fps")) { + g_object_set (G_OBJECT (element), "autoprobe-fps", FALSE, NULL); + } + } + + array = gst_property_probe_probe_and_get_values (probe, pspec); + if (array != NULL) { + + for (n = 0 ; n < array->n_values ; n++) { + GValue *device = g_value_array_get_nth (array, n); + gst_element_set_state (element, GST_STATE_NULL); + + ret = g_list_append(ret, device); + } + } + } + + return ret; +} + +void +purple_media_element_set_device(GstElement *element, GValue *device) +{ + g_object_set_property(G_OBJECT(element), "device", device); +} + +GValue * +purple_media_element_get_device(GstElement *element) +{ + GValue *device; + g_object_get(G_OBJECT(element), "device", &device, NULL); + return device; +} + +GstElement * +purple_media_get_element(const gchar *factory_name) +{ + GstElementFactory *factory = gst_element_factory_find(factory_name); + GstElement *element = gst_element_factory_create(factory, "video_src"); + gst_object_unref(factory); + return element; +} + +void +purple_media_audio_init_src(GstElement **sendbin, GstElement **sendlevel) +{ + GstElement *src; + GstPad *pad; + GstPad *ghost; + const gchar *audio_device = purple_prefs_get_string("/purple/media/audio/device"); + + purple_debug_info("media", "purple_media_audio_init_src\n"); + + *sendbin = gst_bin_new("sendbin"); + src = gst_element_factory_make("alsasrc", "asrc"); + *sendlevel = gst_element_factory_make("level", "sendlevel"); + gst_bin_add_many(GST_BIN(*sendbin), src, *sendlevel, NULL); + gst_element_link(src, *sendlevel); + pad = gst_element_get_pad(*sendlevel, "src"); + ghost = gst_ghost_pad_new("ghostsrc", pad); + gst_element_add_pad(*sendbin, ghost); + g_object_set(G_OBJECT(*sendlevel), "message", TRUE, NULL); + + /* set current audio device on "src"... */ + if (audio_device) { + GList *devices = purple_media_get_devices(src); + GList *dev = devices; + purple_debug_info("media", "Setting device of GstElement src to %s\n", + audio_device); + for (; dev ; dev = dev->next) { + GValue *device = (GValue *) dev->data; + char *name = purple_media_get_device_name(src, device); + if (strcmp(name, audio_device) == 0) { + purple_media_element_set_device(src, device); + } + g_free(name); + } + } +} + +void +purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel) +{ + GstElement *sink; + GstPad *pad, *ghost; + + purple_debug_info("media", "purple_media_audio_init_recv\n"); + + *recvbin = gst_bin_new("pidginrecvbin"); + sink = gst_element_factory_make("alsasink", "asink"); + g_object_set(G_OBJECT(sink), "sync", FALSE, NULL); + *recvlevel = gst_element_factory_make("level", "recvlevel"); + gst_bin_add_many(GST_BIN(*recvbin), sink, *recvlevel, NULL); + gst_element_link(*recvlevel, sink); + pad = gst_element_get_pad(*recvlevel, "sink"); + ghost = gst_ghost_pad_new("ghostsink", pad); + gst_element_add_pad(*recvbin, ghost); + g_object_set(G_OBJECT(*recvlevel), "message", TRUE, NULL); + + purple_debug_info("media", "purple_media_audio_init_recv end\n"); +} + +#endif /* USE_GSTPROPS */ +#endif /* USE_FARSIGHT */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media.h Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,118 @@ +/** + * @file media.h Media API + * @ingroup core + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 __MEDIA_H_ +#define __MEDIA_H_ + +#ifdef USE_FARSIGHT +#ifdef USE_GSTPROPS + +#include <farsight/farsight.h> +#include <glib.h> +#include <glib-object.h> + +#include "connection.h" + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA (purple_media_get_type()) +#define PURPLE_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA, PurpleMedia)) +#define PURPLE_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA, PurpleMediaClass)) +#define PURPLE_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA)) +#define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA)) +#define PURPLE_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass)) + +typedef struct _PurpleMedia PurpleMedia; +typedef struct _PurpleMediaClass PurpleMediaClass; +typedef struct _PurpleMediaPrivate PurpleMediaPrivate; + +typedef enum { + PURPLE_MEDIA_RECV_AUDIO = 1 << 0, + PURPLE_MEDIA_SEND_AUDIO = 1 << 1, + PURPLE_MEDIA_RECV_VIDEO = 1 << 2, + PURPLE_MEDIA_SEND_VIDEO = 1 << 3, +} PurpleMediaStreamType; + +struct _PurpleMediaClass +{ + GObjectClass parent_class; +}; + +struct _PurpleMedia +{ + GObject parent; + PurpleMediaPrivate *priv; +}; + +GType purple_media_get_type(void); + +void purple_media_get_elements(PurpleMedia *media, GstElement **audio_src, GstElement **audio_sink, + GstElement **video_src, GstElement **video_sink); + +void purple_media_set_audio_src(PurpleMedia *media, GstElement *video_src); +void purple_media_set_audio_sink(PurpleMedia *media, GstElement *video_src); +void purple_media_set_video_src(PurpleMedia *media, GstElement *video_src); +void purple_media_set_video_sink(PurpleMedia *media, GstElement *video_src); + +GstElement *purple_media_get_audio_src(PurpleMedia *media); +GstElement *purple_media_get_audio_sink(PurpleMedia *media); +GstElement *purple_media_get_video_src(PurpleMedia *media); +GstElement *purple_media_get_video_sink(PurpleMedia *media); + +GstElement *purple_media_get_audio_pipeline(PurpleMedia *media); + +PurpleConnection *purple_media_get_connection(PurpleMedia *media); +const char *purple_media_get_screenname(PurpleMedia *media); +void purple_media_ready(PurpleMedia *media); +void purple_media_wait(PurpleMedia *media); +void purple_media_accept(PurpleMedia *media); +void purple_media_reject(PurpleMedia *media); +void purple_media_hangup(PurpleMedia *media); +void purple_media_got_hangup(PurpleMedia *media); +void purple_media_got_accept(PurpleMedia *media); + +gchar *purple_media_get_device_name(GstElement *element, + GValue *device); + +GList *purple_media_get_devices(GstElement *element); +void purple_media_element_set_device(GstElement *element, GValue *device); +void purple_media_element_set_device_from_name(GstElement *element, + const gchar *name); +GValue *purple_media_element_get_device(GstElement *element); +GstElement *purple_media_get_element(const gchar *factory_name); + +void purple_media_audio_init_src(GstElement **sendbin, + GstElement **sendlevel); +void purple_media_video_init_src(GstElement **sendbin); + +void purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel); + +G_END_DECLS + +#endif /* USE_GSTPROPS */ +#endif /* USE_FARSIGHT */ + + +#endif /* __MEDIA_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/mediamanager.c Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,147 @@ +/** + * @file mediamanager.c Media Manager API + * @ingroup core + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 "internal.h" + +#include "connection.h" +#include "mediamanager.h" +#include "media.h" + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> + +struct _PurpleMediaManagerPrivate +{ + GList *medias; +}; + +#define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate)) + +static void purple_media_manager_class_init (PurpleMediaManagerClass *klass); +static void purple_media_manager_init (PurpleMediaManager *media); +static void purple_media_manager_finalize (GObject *object); + +static GObjectClass *parent_class = NULL; + + + +enum { + INIT_MEDIA, + LAST_SIGNAL +}; +static guint purple_media_manager_signals[LAST_SIGNAL] = {0}; + +enum { + PROP_0, + PROP_FARSIGHT_SESSION, + PROP_NAME, + PROP_CONNECTION, + PROP_MIC_ELEMENT, + PROP_SPEAKER_ELEMENT, +}; + +GType +purple_media_manager_get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(PurpleMediaManagerClass), + NULL, + NULL, + (GClassInitFunc) purple_media_manager_class_init, + NULL, + NULL, + sizeof(PurpleMediaManager), + 0, + (GInstanceInitFunc) purple_media_manager_init, + NULL + }; + type = g_type_register_static(G_TYPE_OBJECT, "PurpleMediaManager", &info, 0); + } + return type; +} + + +static void +purple_media_manager_class_init (PurpleMediaManagerClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = purple_media_manager_finalize; + + purple_media_manager_signals[INIT_MEDIA] = g_signal_new ("init-media", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, PURPLE_TYPE_MEDIA); + g_type_class_add_private(klass, sizeof(PurpleMediaManagerPrivate)); +} + +static void +purple_media_manager_init (PurpleMediaManager *media) +{ + media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media); + media->priv->medias = NULL; +} + +static void +purple_media_manager_finalize (GObject *media) +{ + parent_class->finalize(media); +} + +PurpleMediaManager * +purple_media_manager_get() +{ + static PurpleMediaManager *manager = NULL; + + if (manager == NULL) + manager = PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL)); + return manager; +} + +PurpleMedia* +purple_media_manager_create_media(PurpleMediaManager *manager, + PurpleConnection *gc, + const char *screenname, + FarsightStream *audio_stream, + FarsightStream *video_stream) +{ + PurpleMedia *media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), + "screenname", screenname, + "connection", gc, + "audio-stream", audio_stream, + "video-stream", video_stream, NULL)); + manager->priv->medias = g_list_append(manager->priv->medias, media); + g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0, media); + return media; +} + +#endif /* USE_FARSIGHT */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/mediamanager.h Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,76 @@ +/** + * @file mediamanager.h Media Manager API + * @ingroup core + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 __MEDIA_MANAGER_H_ +#define __MEDIA_MANAGER_H_ + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> +#include <glib.h> +#include <glib-object.h> + +#include "connection.h" +#include "media.h" + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA_MANAGER (purple_media_manager_get_type()) +#define PURPLE_MEDIA_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManager)) +#define PURPLE_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass)) +#define PURPLE_IS_MEDIA_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_MANAGER)) +#define PURPLE_IS_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_MANAGER)) +#define PURPLE_MEDIA_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass)) + +typedef struct _PurpleMediaManager PurpleMediaManager; +typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass; +typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate; + +struct _PurpleMediaManagerClass +{ + GObjectClass parent_class; +}; + +struct _PurpleMediaManager +{ + GObject parent; + PurpleMediaManagerPrivate *priv; +}; + +GType purple_media_manager_get_type(void); +PurpleMediaManager *purple_media_manager_get(void); + +PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager, + PurpleConnection *gc, + const char *screenname, + FarsightStream *audio_stream, + FarsightStream *video_stream); + +G_END_DECLS + +#endif /* USE_FARSIGHT */ + + +#endif /* __MEDIA_MANAGER_H_ */
--- a/libpurple/plugins/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/plugins/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -140,6 +140,9 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) \ $(GLIB_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(DBUS_CFLAGS)
--- a/libpurple/plugins/perl/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/plugins/perl/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -5,7 +5,7 @@ plugin_LTLIBRARIES = perl.la perl_la_LDFLAGS = -module -avoid-version -perl_la_LIBADD = $(GLIB_LIBS) $(PERL_LIBS) +perl_la_LIBADD = $(GLIB_LIBS) $(PERL_LIBS) $(FARSIGHT_LIBS) perl_la_SOURCES = \ perl.c \ perl-common.c \ @@ -164,4 +164,5 @@ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(PLUGIN_CFLAGS) \ - $(PERL_CFLAGS) + $(PERL_CFLAGS) \ + $(FARSIGHT_CFLAGS)
--- a/libpurple/plugins/ssl/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/plugins/ssl/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -31,6 +31,9 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) \ $(PLUGIN_CFLAGS) ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
--- a/libpurple/plugins/tcl/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/plugins/tcl/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -7,7 +7,7 @@ tcl_la_SOURCES = tcl.c tcl_glib.c tcl_glib.h tcl_cmds.c tcl_signals.c tcl_purple.h \ tcl_ref.c tcl_cmd.c -tcl_la_LIBADD = $(GLIB_LIBS) $(TCL_LIBS) $(TK_LIBS) +tcl_la_LIBADD = $(GLIB_LIBS) $(TCL_LIBS) $(TK_LIBS) $(FARSIGHT_LIBS) EXTRA_DIST = signal-test.tcl Makefile.mingw @@ -18,5 +18,6 @@ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(PLUGIN_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(TK_CFLAGS) \ $(TCL_CFLAGS)
--- a/libpurple/protocols/bonjour/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/bonjour/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -51,5 +51,12 @@ $(GLIB_CFLAGS) \ $(DEBUG_CFLAGS) \ $(LIBXML_CFLAGS) \ - $(AVAHI_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +#if MDNS_AVAHI +# AM_CPPFLAGS += $(AVAHI_CFLAGS) +#else +#endif +
--- a/libpurple/protocols/bonjour/bonjour.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Tue May 27 06:27:46 2008 +0000 @@ -499,13 +499,13 @@ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = @@ -732,3 +732,4 @@ } PURPLE_INIT_PLUGIN(bonjour, init_plugin, info); +
--- a/libpurple/protocols/gg/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/gg/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -75,5 +75,8 @@ -I$(top_builddir)/libpurple \ $(INTGG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/gg/gg.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/gg/gg.c Tue May 27 06:27:46 2008 +0000 @@ -2147,13 +2147,13 @@ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; /* }}} */ @@ -2240,3 +2240,4 @@ PURPLE_INIT_PLUGIN(gg, init_plugin, info); /* vim: set ts=8 sts=0 sw=8 noet: */ +
--- a/libpurple/protocols/irc/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/irc/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -32,4 +32,6 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ $(DEBUG_CFLAGS)
--- a/libpurple/protocols/irc/irc.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/irc/irc.c Tue May 27 06:27:46 2008 +0000 @@ -904,13 +904,13 @@ NULL, /* whiteboard_prpl_ops */ irc_send_raw, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ + NULL /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static gboolean load_plugin (PurplePlugin *plugin) {
--- a/libpurple/protocols/jabber/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -19,6 +19,8 @@ iq.h \ jabber.c \ jabber.h \ + jingle.c \ + jingle.h \ jutil.c \ jutil.h \ message.c \ @@ -69,7 +71,7 @@ pkg_LTLIBRARIES = libjabber.la libxmpp.la noinst_LIBRARIES = -libjabber_la_SOURCES = $(JABBERSOURCES) +libjabber_la_SOURCES = $(JABBERSOURCES) libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) libxmpp_la_SOURCES = libxmpp.c @@ -82,4 +84,6 @@ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ $(LIBXML_CFLAGS)
--- a/libpurple/protocols/jabber/caps.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/caps.c Tue May 27 06:27:46 2008 +0000 @@ -27,6 +27,7 @@ #include "util.h" #include "iq.h" + #define JABBER_CAPS_FILENAME "xmpp-caps.xml" static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */
--- a/libpurple/protocols/jabber/disco.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/disco.c Tue May 27 06:27:46 2008 +0000 @@ -79,7 +79,7 @@ void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { const char *from = xmlnode_get_attrib(packet, "from"); const char *type = xmlnode_get_attrib(packet, "type"); - + if(!from || !type) return; @@ -105,7 +105,7 @@ if(node) xmlnode_set_attrib(query, "node", node); - + if(!node || !strcmp(node, CAPS0115_NODE "#" VERSION)) { identity = xmlnode_new_child(query, "identity"); xmlnode_set_attrib(identity, "category", "client"); @@ -142,6 +142,15 @@ SUPPORT_FEATURE(feat->namespace); } } + } else if (node && !strcmp(node, CAPS0115_NODE "#voice-v1")) { + SUPPORT_FEATURE("http://www.google.com/session"); + SUPPORT_FEATURE("http://www.google.com/transport/p2p"); + SUPPORT_FEATURE("http://www.google.com/transport/raw-udp"); + SUPPORT_FEATURE("http://www.google.com/session/phone"); + SUPPORT_FEATURE("http://www.xmpp.org/extensions/xep-0166.html"); + SUPPORT_FEATURE("http://www.xmpp.org/extensions/xep-0180.html"); + SUPPORT_FEATURE("http://www.xmpp.org/extensions/xep-0167.html"); + SUPPORT_FEATURE("http://www.xmpp.org/extensions/xep-0177.html"); } else { const char *ext = NULL; unsigned pos;
--- a/libpurple/protocols/jabber/google.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/google.c Tue May 27 06:27:46 2008 +0000 @@ -18,8 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include <farsight/farsight-transport.h> + #include "internal.h" #include "debug.h" +#include "mediamanager.h" #include "util.h" #include "privacy.h" @@ -29,6 +32,366 @@ #include "presence.h" #include "iq.h" +typedef struct { + char *id; + char *initiator; +} GoogleSessionId; + +typedef enum { + UNINIT, + SENT_INITIATE, + RECEIVED_INITIATE, + IN_PRORESS, + TERMINATED +} GoogleSessionState; + +typedef struct { + GoogleSessionId id; + GoogleSessionState state; + PurpleMedia *media; + FarsightStream *stream; + JabberStream *js; + char *remote_jid; +} GoogleSession; + +GHashTable *sessions = NULL; + +static guint +google_session_id_hash(gconstpointer key) +{ + GoogleSessionId *id = (GoogleSessionId*)key; + + guint id_hash = g_str_hash(id->id); + guint init_hash = g_str_hash(id->initiator); + + return 23 * id_hash + init_hash; +} + +static gboolean +google_session_id_equal(gconstpointer a, gconstpointer b) +{ + GoogleSessionId *c = (GoogleSessionId*)a; + GoogleSessionId *d = (GoogleSessionId*)b; + + return !strcmp(c->id, d->id) && !strcmp(c->initiator, d->initiator); +} + +static void +google_session_destroy(GoogleSession *session) +{ + g_hash_table_remove(sessions, &(session->id)); + g_free(session->id.id); + g_free(session->id.initiator); + g_free(session->remote_jid); + g_object_unref(session->media); + g_object_unref(session->stream); + g_free(session); +} + +static xmlnode * +google_session_create_xmlnode(GoogleSession *session, const char *type) +{ + xmlnode *node = xmlnode_new("session"); + xmlnode_set_namespace(node, "http://www.google.com/session"); + xmlnode_set_attrib(node, "id", session->id.id); + xmlnode_set_attrib(node, "initiator", session->id.initiator); + xmlnode_set_attrib(node, "type", type); + return node; +} + +static void +google_session_send_accept(GoogleSession *session) +{ + xmlnode *sess, *desc, *payload; + GList *codecs = farsight_stream_get_codec_intersection(session->stream); + JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); + + xmlnode_set_attrib(iq->node, "to", session->remote_jid); + sess = google_session_create_xmlnode(session, "accept"); + xmlnode_insert_child(iq->node, sess); + desc = xmlnode_new_child(sess, "description"); + xmlnode_set_namespace(desc, "http://www.google.com/session/phone"); + + for (;codecs; codecs = codecs->next) { + FarsightCodec *codec = (FarsightCodec*)codecs->data; + char id[8], clockrate[10]; + payload = xmlnode_new_child(desc, "payload-type"); + g_snprintf(id, sizeof(id), "%d", codec->id); + g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate); + xmlnode_set_attrib(payload, "name", codec->encoding_name); + xmlnode_set_attrib(payload, "id", id); + xmlnode_set_attrib(payload, "clockrate", clockrate); + } + + jabber_iq_send(iq); + farsight_stream_start(session->stream); +} + +static void +google_session_send_terminate(GoogleSession *session) +{ + xmlnode *sess; + GList *codecs = farsight_stream_get_codec_intersection(session->stream); + JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); + + xmlnode_set_attrib(iq->node, "to", session->remote_jid); + sess = google_session_create_xmlnode(session, "terminate"); + xmlnode_insert_child(iq->node, sess); + + jabber_iq_send(iq); + farsight_stream_stop(session->stream); + google_session_destroy(session); +} + +static void +google_session_send_reject(GoogleSession *session) +{ + xmlnode *sess; + GList *codecs = farsight_stream_get_codec_intersection(session->stream); + JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); + + xmlnode_set_attrib(iq->node, "to", session->remote_jid); + sess = google_session_create_xmlnode(session, "reject"); + xmlnode_insert_child(iq->node, sess); + + jabber_iq_send(iq); + farsight_stream_stop(session->stream); + google_session_destroy(session); +} + + +static void +google_session_candidates_prepared (FarsightStream *stream, gchar *candidate_id, GoogleSession *session) +{ + JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); + GList *candidates = farsight_stream_get_native_candidate_list(stream); + FarsightTransportInfo *transport; + xmlnode *sess; + xmlnode *candidate; + sess = google_session_create_xmlnode(session, "candidates"); + xmlnode_insert_child(iq->node, sess); + xmlnode_set_attrib(iq->node, "to", session->remote_jid); + + for (;candidates;candidates = candidates->next) { + transport = (FarsightTransportInfo*)(candidates->data); + char port[8]; + char pref[8]; + + if (!strcmp(transport->ip, "127.0.0.1")) + continue; + + candidate = xmlnode_new("candidate"); + + g_snprintf(port, sizeof(port), "%d", transport->port); + g_snprintf(pref, sizeof(pref), "%f", transport->preference); + + xmlnode_set_attrib(candidate, "address", transport->ip); + xmlnode_set_attrib(candidate, "port", port); + xmlnode_set_attrib(candidate, "name", "rtp"); + xmlnode_set_attrib(candidate, "username", transport->username); + xmlnode_set_attrib(candidate, "password", transport->password); + xmlnode_set_attrib(candidate, "preference", pref); + xmlnode_set_attrib(candidate, "protocol", transport->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ? "udp" : "tcp"); + xmlnode_set_attrib(candidate, "type", transport->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? "local" : + transport->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? "stun" : + transport->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? "relay" : NULL); + xmlnode_set_attrib(candidate, "generation", "0"); + xmlnode_set_attrib(candidate, "network", "0"); + xmlnode_insert_child(sess, candidate); + + } + jabber_iq_send(iq); +} + +static void +google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +{ + PurpleMedia *media; + JabberIq *result; + FarsightSession *fs; + GList *codecs = NULL; + xmlnode *desc_element, *codec_element; + FarsightCodec *codec; + const char *id, *encoding_name, *clock_rate; + int res; + + if (session->state != UNINIT) { + purple_debug_error("jabber", "Received initiate for active session.\n"); + return FALSE; + } + + fs = farsight_session_factory_make("rtp"); + if (!fs) { + purple_debug_error("jabber", "Farsight's rtp plugin not installed"); + return FALSE; + } + + session->stream = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, FARSIGHT_STREAM_DIRECTION_BOTH); + + g_object_set(G_OBJECT(session->stream), "transmitter", "libjingle", NULL); + + desc_element = xmlnode_get_child(sess, "description"); + + for (codec_element = xmlnode_get_child(desc_element, "payload-type"); + codec_element; + codec_element = xmlnode_get_next_twin(codec_element)) { + encoding_name = xmlnode_get_attrib(codec_element, "name"); + id = xmlnode_get_attrib(codec_element, "id"); + clock_rate = xmlnode_get_attrib(codec_element, "clockrate"); + + codec = g_new0(FarsightCodec, 1); + farsight_codec_init(codec, atoi(id), encoding_name, FARSIGHT_MEDIA_TYPE_AUDIO, clock_rate ? atoi(clock_rate) : 0); + codecs = g_list_append(codecs, codec); + } + + session->media = media = purple_media_manager_create_media(purple_media_manager_get(), js->gc, session->remote_jid, session->stream, NULL); + + g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(google_session_send_accept), session); + g_signal_connect_swapped(G_OBJECT(media), "reject", G_CALLBACK(google_session_send_reject), session); + g_signal_connect_swapped(G_OBJECT(media), "hangup", G_CALLBACK(google_session_send_terminate), session); + + + GstElement *e = purple_media_get_audio_src(media); + farsight_stream_set_source(session->stream, e); + + e = purple_media_get_audio_sink(media); + farsight_stream_set_sink(session->stream, e); + + farsight_stream_prepare_transports(session->stream); + res = farsight_stream_set_remote_codecs(session->stream, codecs); + + purple_media_ready(media); + + farsight_codec_list_destroy(codecs); + g_signal_connect(G_OBJECT(session->stream), "new-native-candidate", G_CALLBACK(google_session_candidates_prepared), session); + result = jabber_iq_new(js, JABBER_IQ_RESULT); + jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "to", session->remote_jid); + jabber_iq_send(result); +} + +static void +google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +{ + JabberIq *result; + GList *list = NULL; + xmlnode *cand; + static int name = 0; + char n[4]; + + for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) { + FarsightTransportInfo *info = g_new0(FarsightTransportInfo, 1); + g_snprintf(n, sizeof(n), "S%d", name++); + info->ip = xmlnode_get_attrib(cand, "address"); + info->port = atoi(xmlnode_get_attrib(cand, "port")); + info->proto = !strcmp(xmlnode_get_attrib(cand, "protocol"),"udp") ? FARSIGHT_NETWORK_PROTOCOL_UDP : FARSIGHT_NETWORK_PROTOCOL_TCP; + info->preference = atof(xmlnode_get_attrib(cand, "preference")); + info->type = !strcmp(xmlnode_get_attrib(cand, "type"), "local") ? FARSIGHT_CANDIDATE_TYPE_LOCAL : + !strcmp(xmlnode_get_attrib(cand, "type"), "stun") ? FARSIGHT_CANDIDATE_TYPE_DERIVED : + !strcmp(xmlnode_get_attrib(cand, "type"), "relay") ? FARSIGHT_CANDIDATE_TYPE_RELAY : FARSIGHT_CANDIDATE_TYPE_LOCAL; + info->candidate_id = n; + info->username = xmlnode_get_attrib(cand, "username"); + info->password = xmlnode_get_attrib(cand, "password"); + list = g_list_append(list, info); + } + + farsight_stream_add_remote_candidate(session->stream, list); + g_list_foreach(list, g_free, NULL); + g_list_free(list); + + result = jabber_iq_new(js, JABBER_IQ_RESULT); + jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "to", session->remote_jid); + jabber_iq_send(result); +} + +static void +google_session_handle_reject(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +{ + farsight_stream_stop(session->stream); + purple_media_got_hangup(session->media); + + google_session_destroy(session); +} + +static void +google_session_handle_terminate(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +{ + farsight_stream_stop(session->stream); + purple_media_got_hangup(session->media); + + google_session_destroy(session); +} + +static void +google_session_parse_iq(JabberStream *js, GoogleSession *session, xmlnode *packet) +{ + xmlnode *sess = xmlnode_get_child(packet, "session"); + const char *type = xmlnode_get_attrib(sess, "type"); + + if (!strcmp(type, "initiate")) { + google_session_handle_initiate(js, session, packet, sess); + } else if (!strcmp(type, "accept")) { + } else if (!strcmp(type, "reject")) { + google_session_handle_reject(js, session, packet, sess); + } else if (!strcmp(type, "terminate")) { + google_session_handle_terminate(js, session, packet, sess); + } else if (!strcmp(type, "candidates")) { + google_session_handle_candidates(js, session, packet, sess); + } +} + +void +jabber_google_session_parse(JabberStream *js, xmlnode *packet) +{ + GoogleSession *session; + GoogleSessionId id; + JabberIq *result; + + xmlnode *session_node; + xmlnode *desc_node; + + if (strcmp(xmlnode_get_attrib(packet, "type"), "set")) + return; + + session_node = xmlnode_get_child(packet, "session"); + if (!session_node) + return; + + id.id = xmlnode_get_attrib(session_node, "id"); + if (!id.id) + return; + + id.initiator = xmlnode_get_attrib(session_node, "initiator"); + if (!id.initiator) + return; + + if (sessions == NULL) + sessions = g_hash_table_new(google_session_id_hash, google_session_id_equal); + session = (GoogleSession*)g_hash_table_lookup(sessions, &id); + + if (session) { + google_session_parse_iq(js, session, packet); + return; + } + + /* If the session doesn't exist, this has to be an initiate message */ + if (strcmp(xmlnode_get_attrib(session_node, "type"), "initiate")) + return; + desc_node = xmlnode_get_child(session_node, "description"); + if (!desc_node) + return; + session = g_new0(GoogleSession, 1); + session->id.id = g_strdup(id.id); + session->id.initiator = g_strdup(id.initiator); + session->state = UNINIT; + session->js = js; + session->remote_jid = g_strdup(session->id.initiator); + g_hash_table_insert(sessions, &(session->id), session); + + google_session_parse_iq(js, session, packet); +} + static void jabber_gmail_parse(JabberStream *js, xmlnode *packet, gpointer nul) {
--- a/libpurple/protocols/jabber/google.h Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/google.h Tue May 27 06:27:46 2008 +0000 @@ -45,6 +45,7 @@ char *jabber_google_format_to_html(const char *text); +void jabber_google_session_parse(JabberStream *js, xmlnode *node); #endif /* _PURPLE_GOOGLE_H_ */
--- a/libpurple/protocols/jabber/iq.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/iq.c Tue May 27 06:27:46 2008 +0000 @@ -313,7 +313,7 @@ const char *xmlns; const char *type, *id, *from; JabberIqHandler *jih; - + query = xmlnode_get_child(packet, "query"); type = xmlnode_get_attrib(packet, "type"); from = xmlnode_get_attrib(packet, "from"); @@ -337,6 +337,11 @@ return; } } + + if (xmlnode_get_child_with_namespace(packet, "session", "http://www.google.com/session")) { + jabber_google_session_parse(js, packet); + return; + } if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) { jabber_si_parse(js, packet); @@ -348,12 +353,37 @@ return; } - purple_debug_info("jabber", "jabber_iq_parse\n"); - if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) { jabber_ping_parse(js, packet); return; } + +#ifdef USE_FARSIGHT + /* handle session initiate XEP 0167 */ + if (type && !strcmp(type, "set")) { + /* is this a Jingle package? */ + xmlnode *jingle = xmlnode_get_child(packet, "jingle"); + if (jingle) { + const char *action = xmlnode_get_attrib(jingle, "action"); + purple_debug_info("jabber", "got Jingle package action = %s\n", + action); + if (!strcmp(action, "session-initiate")) { + jabber_handle_session_initiate(js, packet); + } else if (!strcmp(action, "session-accept") + || !strcmp(action, "content-accept")) { + jabber_handle_session_accept(js, packet); + } else if (!strcmp(action, "session-terminate")) { + jabber_handle_session_terminate(js, packet); + } else if (!strcmp(action, "transport-info")) { + jabber_handle_session_candidates(js, packet); + } else if (!strcmp(action, "content-replace")) { + jabber_handle_session_content_replace(js, packet); + } + + return; + } + } +#endif /* If we get here, send the default error reply mandated by XMPP-CORE */ if(type && (!strcmp(type, "set") || !strcmp(type, "get"))) {
--- a/libpurple/protocols/jabber/jabber.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Tue May 27 06:27:46 2008 +0000 @@ -56,6 +56,7 @@ #include "xdata.h" #include "pep.h" #include "adhoccommands.h" +#include "jingle.h" #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) @@ -2343,6 +2344,564 @@ return TRUE; } +#ifdef USE_FARSIGHT + +static void +jabber_session_send_accept(JingleSession *session) +{ + JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), + JABBER_IQ_SET); + xmlnode *jingle = jabber_jingle_session_create_session_accept(session); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); + purple_debug_info("jabber", "Sent session accept, starting stream\n"); + farsight_stream_start(jabber_jingle_session_get_stream(session)); + farsight_stream_set_remote_codecs( + jabber_jingle_session_get_stream(session), + jabber_jingle_session_get_remote_codecs(session)); + +} + +static void +jabber_session_send_content_accept(JingleSession *session) +{ + JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), + JABBER_IQ_SET); + xmlnode *jingle = jabber_jingle_session_create_content_accept(session); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); +} + +static void +jabber_session_send_reject(JingleSession *session) +{ + JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), + JABBER_IQ_SET); + xmlnode *jingle = jabber_jingle_session_create_terminate(session, + "decline", NULL); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); + farsight_stream_stop(jabber_jingle_session_get_stream(session)); + jabber_jingle_session_destroy(session); +} + +static void +jabber_session_send_terminate(JingleSession *session) +{ + JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), + JABBER_IQ_SET); + xmlnode *jingle = jabber_jingle_session_create_terminate(session, + "no-error", NULL); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); + farsight_stream_stop(jabber_jingle_session_get_stream(session)); + jabber_jingle_session_destroy(session); +} + +/* callback called when new local transport candidate(s) are available on the + Farsight stream */ +static void +jabber_session_candidates_prepared(FarsightStream *stream, gchar *candidate_id, + JingleSession *session) +{ + /* create transport-info package */ + JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), + JABBER_IQ_SET); + xmlnode *jingle = jabber_jingle_session_create_transport_info(session, + candidate_id); + purple_debug_info("jabber", "jabber_session_candidates_prepared called for candidate_id = %s\n", + candidate_id); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); +} + +/* callback called when a pair of transport candidates (local and remote) + has been established */ +static void +jabber_session_candidate_pair_established(FarsightStream *stream, + gchar *native_candidate_id, + gchar *remote_candidate_id, + JingleSession *session) +{ + purple_debug_info("jabber", "jabber_candidate_pair_established called"); + /* if we are the initiator, we should send a content-modify message */ + if (jabber_jingle_session_is_initiator(session)) { + purple_debug_info("jabber", + "we are the initiator, let's send conten-modify\n"); + JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session), + JABBER_IQ_SET); + /* shall change this to a "content-replace" */ + xmlnode *jingle = + jabber_jingle_session_create_content_replace(session, + native_candidate_id, + remote_candidate_id); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); + } + /* + farsight_stream_set_active_candidate_pair(stream, native_candidate_id, + remote_candidate_id); + */ +} + + +PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, + PurpleMediaStreamType type) +{ + /* create content negotiation */ + JabberStream *js = gc->proto_data; + JabberIq *request = jabber_iq_new(js, JABBER_IQ_SET); + xmlnode *jingle, *content, *description, *payload_type, *transport; + FarsightSession *fs = NULL; + FarsightStream *audio = NULL; /* only audio for now... */ + GList *codecs; + GList *codec_iter = NULL; + FarsightCodec *codec = NULL; + PurpleMedia *media = NULL; + JingleSession *session; + JabberBuddy *jb; + JabberBuddyResource *jbr; + + char id[10]; + char clock_rate[10]; + char channels[10]; + char jid[256]; + char me[256]; + + /* for debug */ + char *output; + int len; + + /* setup stream */ + fs = farsight_session_factory_make("rtp"); + if (fs == NULL) { + purple_debug_error("jabber", "Farsight's rtp plugin not installed"); + return NULL; + } + + /* check media stream type, and so on... */ + /* only do audio for now... */ + + /* get stuff from Farsight... */ + audio = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, + FARSIGHT_STREAM_DIRECTION_BOTH); + g_object_set(G_OBJECT(audio), "transmitter", "libjingle", NULL); + + purple_debug_info("jabber", "Getting local codecs\n"); + codecs = farsight_stream_get_local_codecs(audio); + purple_debug_info("jabber", "number of codecs: %d\n", g_list_length(codecs)); + + if (audio == NULL) { + purple_debug_error("jabber", "Unable to create Farsight stream for audio"); + /* destroy FarsightSession? */ + return NULL; + } + + media = purple_media_manager_create_media(purple_media_manager_get(), + gc, who, audio, NULL); + purple_debug_info("jabber", "After purple_media_manager_create_media\n"); + /* construct JID to send to */ + jb = jabber_buddy_find(js, who, FALSE); + if (!jb) { + purple_debug_error("jabber", "Could not find Jabber buddy\n"); + return NULL; + } + jbr = jabber_buddy_find_resource(jb, NULL); + if (!jbr) { + purple_debug_error("jabber", "Could not find buddy's resource\n"); + } + + g_snprintf(jid, 255, "%s/%s", who, jbr->name); + + session = jabber_jingle_session_create(js); + jabber_jingle_session_set_remote_jid(session, jid); + /* set ourselves as initiator */ + g_snprintf(me, sizeof(me), "%s@%s/%s", js->user->node, js->user->domain, + js->user->resource); + jabber_jingle_session_set_initiator(session, me); + + jabber_jingle_session_set_stream(session, audio); + jabber_jingle_session_set_media(session, media); + + g_signal_connect_swapped(G_OBJECT(media), "accepted", + G_CALLBACK(jabber_session_send_accept), session); + g_signal_connect_swapped(G_OBJECT(media), "reject", + G_CALLBACK(jabber_session_send_reject), session); + g_signal_connect_swapped(G_OBJECT(media), "hangup", + G_CALLBACK(jabber_session_send_terminate), session); + + GstElement *e = purple_media_get_audio_src(media); + farsight_stream_set_source(jabber_jingle_session_get_stream(session), e); + e = purple_media_get_audio_sink(media); + farsight_stream_set_sink(jabber_jingle_session_get_stream(session), e); + + farsight_stream_prepare_transports(audio); + /* callback for new native (local) transport candidates for the stream */ + g_signal_connect(G_OBJECT(audio), + "new-native-candidate", + G_CALLBACK(jabber_session_candidates_prepared), session); + /* callback for new active candidate pair (established connection) */ + g_signal_connect(G_OBJECT(audio), + "new-active-candidate-pair", + G_CALLBACK(jabber_session_candidate_pair_established), + session); + + /* create request */ + + xmlnode_set_attrib(request->node, "to", + jabber_jingle_session_get_remote_jid(session)); + jingle = xmlnode_new_child(request->node, "jingle"); + xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle"); + xmlnode_set_attrib(jingle, "action", "session-initiate"); + /* get our JID and a session id... */ + xmlnode_set_attrib(jingle, "initiator", me); + xmlnode_set_attrib(jingle, "sid", jabber_jingle_session_get_id(session)); + + content = xmlnode_new_child(jingle, "content"); + xmlnode_set_attrib(content, "name", "audio-content"); + xmlnode_set_attrib(content, "profile", "RTP/AVP"); + + description = xmlnode_new_child(content, "description"); + xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp"); + + /* create payload-type nodes */ + purple_debug_info("jabber", "Generating payload_type elements\n"); + for (; codecs ; codecs = codecs->next) { + codec = (FarsightCodec *) codecs->data; + purple_debug_info("jabber", "Generating payload_type for (%d) %s\n", + codec->id, codec->encoding_name); + sprintf(id, "%d", codec->id); + sprintf(clock_rate, "%d", codec->clock_rate); + sprintf(channels, "%d", codec->channels); + + payload_type = xmlnode_new_child(description, "payload-type"); + xmlnode_set_attrib(payload_type, "id", id); + xmlnode_set_attrib(payload_type, "name", codec->encoding_name); + xmlnode_set_attrib(payload_type, "clockrate", clock_rate); + xmlnode_set_attrib(payload_type, "channels", channels); + } + + transport = xmlnode_new_child(content, "transport"); + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp"); + + /* send request to other part */ + jabber_iq_send(request); + + return media; +} + +gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, + PurpleMediaStreamType type) +{ + return TRUE; +} + + +void +jabber_handle_session_accept(JabberStream *js, xmlnode *packet) +{ + JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); + xmlnode *jingle = xmlnode_get_child(packet, "jingle"); + xmlnode *content = xmlnode_get_child(jingle, "content"); + const char *sid = xmlnode_get_attrib(jingle, "sid"); + const char *action = xmlnode_get_attrib(jingle, "action"); + JingleSession *session = jabber_jingle_session_find_by_id(sid); + FarsightStream *stream = jabber_jingle_session_get_stream(session); + GList *remote_codecs = NULL; + GList *remote_transports = NULL; + GList *codec_intersection = NULL; + FarsightCodec *top = NULL; + xmlnode *description = NULL; + xmlnode *transport = NULL; + + /* We should probably check validity of the incoming XML... */ + + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + + description = xmlnode_get_child(jingle, "description"); + transport = xmlnode_get_child(content, "transport"); + + /* fetch codecs from remote party */ + purple_debug_info("jabber", "get codecs from session-accept\n"); + remote_codecs = jabber_jingle_get_codecs(description); + purple_debug_info("jabber", "get transport candidates from session accept\n"); + remote_transports = jabber_jingle_get_candidates(transport); + + purple_debug_info("jabber", "Got %d codecs from responder\n", + g_list_length(remote_codecs)); + purple_debug_info("jabber", "Got %d transport candidates from responder\n", + g_list_length(remote_transports)); + + purple_debug_info("jabber", "Setting remote codecs on stream\n"); + + farsight_stream_set_remote_codecs(stream, remote_codecs); + if (!strcmp(action, "session-accept")) { + jabber_jingle_session_set_remote_codecs(session, remote_codecs); + } + + codec_intersection = farsight_stream_get_codec_intersection(stream); + purple_debug_info("jabber", "codec_intersection contains %d elems\n", + g_list_length(codec_intersection)); + /* get the top codec */ + if (g_list_length(codec_intersection) > 0) { + top = (FarsightCodec *) codec_intersection->data; + purple_debug_info("jabber", "setting active codec on stream = %d\n", + top->id); + farsight_stream_set_active_codec(stream, top->id); + /* we have found a suitable codec, but we will not start the stream + just yet, wait for transport negotiation to complete... */ + } + /* if we also got transport candidates, add them to our streams + list of known remote candidates */ + if (g_list_length(remote_transports) > 0) { + farsight_stream_set_remote_candidate_list(stream, remote_transports); + } + if (g_list_length(codec_intersection) == 0 && + g_list_length(remote_transports)) { + /* we didn't get any candidates and the codec intersection is empty, + this means this was not a content-accept message and we couldn't + find any suitable codecs, should return error and hang up */ + + } + + g_list_free(codec_intersection); + + if (!strcmp(action, "session-accept")) { + purple_media_got_accept(jabber_jingle_session_get_media(session)); + purple_debug_info("jabber", "Got session-accept, starting stream\n"); + farsight_stream_start(jabber_jingle_session_get_stream(session)); + } + + jabber_iq_send(result); +} + +void +jabber_handle_session_terminate(JabberStream *js, xmlnode *packet) +{ + JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET); + xmlnode *jingle = xmlnode_get_child(packet, "jingle"); + const char *sid = xmlnode_get_attrib(jingle, "sid"); + JingleSession *session = jabber_jingle_session_find_by_id(sid); + + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + + + + /* maybe we should look at the reasoncode to determine if it was + a hangup or a reject, and call different callbacks to purple_media */ + + + purple_media_got_hangup(jabber_jingle_session_get_media(session)); + jabber_iq_send(result); + farsight_stream_stop(jabber_jingle_session_get_stream(session)); + jabber_jingle_session_destroy(session); +} + +void +jabber_handle_session_candidates(JabberStream *js, xmlnode *packet) +{ + JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); + xmlnode *jingle = xmlnode_get_child(packet, "jingle"); + xmlnode *content = xmlnode_get_child(jingle, "content"); + xmlnode *transport = xmlnode_get_child(content, "transport"); + GList *remote_candidates = jabber_jingle_get_candidates(transport); + const char *sid = xmlnode_get_attrib(jingle, "sid"); + JingleSession *session = jabber_jingle_session_find_by_id(sid); + + /* send acknowledement */ + xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from")); + jabber_iq_send(result); + + /* add candidates to our list of remote candidates */ + if (g_list_length(remote_candidates) > 0) { + farsight_stream_add_remote_candidate( + jabber_jingle_session_get_stream(session), + remote_candidates); + jabber_jingle_session_add_remote_candidate(session, remote_candidates); + } +} + +/* change this to content-replace */ +void +jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet) +{ + JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); + JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET); + xmlnode *content_accept = NULL; + xmlnode *jingle = xmlnode_get_child(packet, "jingle"); + const char *sid = xmlnode_get_attrib(jingle, "sid"); + JingleSession *session = jabber_jingle_session_find_by_id(sid); + + /* send acknowledement */ + xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from")); + jabber_iq_send(result); + + /* send content-accept */ + content_accept = jabber_jingle_session_create_content_accept(session); + xmlnode_set_attrib(accept->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(accept->node, "to", xmlnode_get_attrib(packet, "from")); + xmlnode_insert_child(accept->node, content_accept); + + jabber_iq_send(accept); +} + +void +jabber_handle_session_initiate(JabberStream *js, xmlnode *packet) +{ + JingleSession *session = NULL; + xmlnode *jingle = xmlnode_get_child(packet, "jingle"); + xmlnode *content = NULL; + xmlnode *description = NULL; + char *sid = NULL; + char *initiator = NULL; + GList *codecs = NULL; + FarsightSession *fs = NULL; + FarsightStream *audio = NULL; + PurpleMedia *media = NULL; + JabberIq *result = NULL; + JabberIq *content_accept = NULL; + xmlnode *content_accept_jingle = NULL; + GList *codec_intersection = NULL; + + int res; + + if (!jingle) { + purple_debug_error("jabber", "Malformed request"); + return; + } + + sid = xmlnode_get_attrib(jingle, "sid"); + //initiator = xmlnode_get_attrib(jingle, "initiator"); + initiator = xmlnode_get_attrib(packet, "from"); + session = jabber_jingle_session_create_by_id(js, sid); + jabber_jingle_session_set_remote_jid(session, + xmlnode_get_attrib(packet, "from")); + /* set "from" as iniator (we are responder) */ + jabber_jingle_session_set_initiator(session, + xmlnode_get_attrib(packet, "from")); + + /* init media */ + content = xmlnode_get_child(jingle, "content"); + if (!content) { + purple_debug_error("jabber", "jingle tag must contain content tag\n"); + /* should send error here */ + return; + } + + description = xmlnode_get_child(content, "description"); + + if (!description) { + purple_debug_error("jabber", "content tag must contain description tag"); + /* we should create an error iq here */ + return; + } + + codecs = jabber_jingle_get_codecs(description); + + fs = farsight_session_factory_make("rtp"); + if (!fs) { + purple_debug_error("jabber", + "Could not initialize Farsight's RTP plugin"); + return; + } + + audio = farsight_session_create_stream(fs, + FARSIGHT_MEDIA_TYPE_AUDIO, + FARSIGHT_STREAM_DIRECTION_BOTH); + + g_object_set(G_OBJECT(audio), "transmitter", "libjingle", NULL); + + media = purple_media_manager_create_media(purple_media_manager_get(), + js->gc, initiator, audio, NULL); + jabber_jingle_session_set_media(session, media); + jabber_jingle_session_set_stream(session, audio); + + + g_signal_connect_swapped(G_OBJECT(media), "accepted", + G_CALLBACK(jabber_session_send_accept), session); + g_signal_connect_swapped(G_OBJECT(media), "reject", + G_CALLBACK(jabber_session_send_reject), session); + g_signal_connect_swapped(G_OBJECT(media), "hangup", + G_CALLBACK(jabber_session_send_terminate), session); + + + GstElement *e = purple_media_get_audio_src(media); + farsight_stream_set_source(jabber_jingle_session_get_stream(session), e); + + e = purple_media_get_audio_sink(media); + farsight_stream_set_sink(jabber_jingle_session_get_stream(session), e); + + farsight_stream_prepare_transports(jabber_jingle_session_get_stream(session)); + /* For some reason Farsight starts the stream immediatly when calling + farsight_stream_set_remote_codecs, before having called farsight_stream_start + As a "workaround" (maybe this gets fixed in FS2) I'll store the list in + the session to call it later when accepting the call */ + /* + res = + farsight_stream_set_remote_codecs(jabber_jingle_session_get_stream(session), + codecs); + */ + jabber_jingle_session_set_remote_codecs(session, codecs); + + purple_media_ready(media); + + /* We store the remote candidates in the session object... */ + /* + farsight_codec_list_destroy(codecs); + */ + + /* callback for new native (local) transport candidates for the stream */ + g_signal_connect(G_OBJECT(jabber_jingle_session_get_stream(session)), + "new-native-candidate", + G_CALLBACK(jabber_session_candidates_prepared), session); + /* callback for new active candidate pair (established connection) */ + g_signal_connect(G_OBJECT(jabber_jingle_session_get_stream(session)), + "new-active-candidate-pair", + G_CALLBACK(jabber_session_candidate_pair_established), + session); + + result = jabber_iq_new(js, JABBER_IQ_RESULT); + jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from")); + jabber_iq_send(result); + + /* should send a content-accept */ + /* It crashes after this gets sent, also the id of this iq is set to + "purple0", that seems odd... maybe I'm making some mistake here... */ + /* + content_accept = jabber_iq_new(jabber_jingle_session_get_stream(session), + JABBER_IQ_SET); + content_accept_jingle = jabber_jingle_session_create_content_accept(session); + xmlnode_set_attrib(content_accept->node, "to", + jabber_jingle_session_get_remote_jid(session)); + xmlnode_insert_child(content_accept->node, content_accept_jingle); + jabber_iq_send(content_accept); + */ +} + +#endif + void jabber_register_commands(void) { purple_cmd_register("config", "", PURPLE_CMD_P_PRPL, @@ -2430,5 +2989,5 @@ void jabber_init_plugin(PurplePlugin *plugin) { - my_protocol = plugin; + my_protocol = plugin; }
--- a/libpurple/protocols/jabber/jabber.h Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.h Tue May 27 06:27:46 2008 +0000 @@ -55,6 +55,8 @@ #include "dnssrv.h" #include "roomlist.h" #include "sslconn.h" +#include "media.h" +#include "mediamanager.h" #include "jutil.h" #include "xmlnode.h" @@ -268,4 +270,17 @@ void jabber_register_commands(void); void jabber_init_plugin(PurplePlugin *plugin); +#ifdef USE_FARSIGHT +PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, PurpleMediaStreamType type); +gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, PurpleMediaStreamType type); + +/* Jingle handle session messages */ +void jabber_handle_session_initiate(JabberStream *js, xmlnode *packet); +void jabber_handle_session_accept(JabberStream *js, xmlnode *packet); +void jabber_handle_session_terminate(JabberStream *js, xmlnode *packet); +void jabber_handle_session_candidates(JabberStream *js, xmlnode *packet); +void jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet); + +#endif + #endif /* _PURPLE_JABBER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle.c Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,579 @@ +/* + * purple - Jabber Protocol Plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "config.h" +#include "purple.h" +#include "jingle.h" +#include "xmlnode.h" + +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> +#include <farsight/farsight-transport.h> + +/* keep a hash table of JingleSessions */ +static GHashTable *sessions = NULL; + +static gboolean +jabber_jingle_session_equal(gconstpointer a, gconstpointer b) +{ + purple_debug_info("jingle", + "jabber_jingle_session_equal, comparing %s and %s\n", + ((JingleSession *)a)->id, + ((JingleSession *)b)->id); + return !strcmp(((JingleSession *) a)->id, ((JingleSession *) b)->id); +} + +static JingleSession * +jabber_jingle_session_create_internal(JabberStream *js, + const char *id) +{ + JingleSession *sess = g_new0(JingleSession, 1); + sess->js = js; + + if (id) { + sess->id = g_strdup(id); + } else if (js) { + /* init the session ID... */ + sess->id = jabber_get_next_id(js); + } + + /* insert it into the hash table */ + if (!sessions) { + purple_debug_info("jingle", "Creating hash table for sessions\n"); + sessions = g_hash_table_new(g_str_hash, g_str_equal); + } + purple_debug_info("jingle", "inserting session with key: %s into table\n", + sess->id); + g_hash_table_insert(sessions, sess->id, sess); + + sess->remote_candidates = NULL; + sess->remote_codecs = NULL; + + return sess; +} + +JabberStream * +jabber_jingle_session_get_js(const JingleSession *sess) +{ + return sess->js; +} + +JingleSession * +jabber_jingle_session_create(JabberStream *js) +{ + JingleSession *sess = jabber_jingle_session_create_internal(js, NULL); + sess->is_initiator = TRUE; + return sess; +} + +JingleSession * +jabber_jingle_session_create_by_id(JabberStream *js, const char *id) +{ + JingleSession *sess = jabber_jingle_session_create_internal(js, id); + sess->is_initiator = FALSE; + return sess; +} + +const char * +jabber_jingle_session_get_id(const JingleSession *sess) +{ + return sess->id; +} + +void +jabber_jingle_session_destroy(JingleSession *sess) +{ + g_hash_table_remove(sessions, sess->id); + g_free(sess->id); + farsight_codec_list_destroy(sess->remote_codecs); + g_list_free(sess->remote_candidates); + g_free(sess); +} + +JingleSession * +jabber_jingle_session_find_by_id(const char *id) +{ + purple_debug_info("jingle", "find_by_id %s\n", id); + purple_debug_info("jingle", "hash table: %lx\n", sessions); + purple_debug_info("jingle", "hash table size %d\n", + g_hash_table_size(sessions)); + purple_debug_info("jingle", "lookup: %lx\n", g_hash_table_lookup(sessions, id)); + return (JingleSession *) g_hash_table_lookup(sessions, id); +} + +GList * +jabber_jingle_get_codecs(const xmlnode *description) +{ + GList *codecs = NULL; + xmlnode *codec_element = NULL; + const char *encoding_name,*id, *clock_rate; + FarsightCodec *codec; + + for (codec_element = xmlnode_get_child(description, "payload-type") ; + codec_element ; + codec_element = xmlnode_get_next_twin(codec_element)) { + encoding_name = xmlnode_get_attrib(codec_element, "name"); + id = xmlnode_get_attrib(codec_element, "id"); + clock_rate = xmlnode_get_attrib(codec_element, "clockrate"); + + codec = g_new0(FarsightCodec, 1); + farsight_codec_init(codec, atoi(id), encoding_name, + FARSIGHT_MEDIA_TYPE_AUDIO, + clock_rate ? atoi(clock_rate) : 0); + codecs = g_list_append(codecs, codec); + } + return codecs; +} + +GList * +jabber_jingle_get_candidates(const xmlnode *transport) +{ + GList *candidates = NULL; + xmlnode *candidate = NULL; + FarsightTransportInfo *ti; + + for (candidate = xmlnode_get_child(transport, "candidate") ; + candidate ; + candidate = xmlnode_get_next_twin(candidate)) { + const char *type = xmlnode_get_attrib(candidate, "type"); + ti = g_new0(FarsightTransportInfo, 1); + ti->component = atoi(xmlnode_get_attrib(candidate, "component")); + ti->ip = xmlnode_get_attrib(candidate, "ip"); + ti->port = atoi(xmlnode_get_attrib(candidate, "port")); + ti->proto = strcmp(xmlnode_get_attrib(candidate, "protocol"), + "udp") == 0 ? + FARSIGHT_NETWORK_PROTOCOL_UDP : + FARSIGHT_NETWORK_PROTOCOL_TCP; + /* it seems Farsight RTP doesn't handle this correctly right now */ + ti->username = xmlnode_get_attrib(candidate, "ufrag"); + ti->password = xmlnode_get_attrib(candidate, "pwd"); + /* not quite sure about this */ + ti->type = strcmp(type, "host") == 0 ? + FARSIGHT_CANDIDATE_TYPE_LOCAL : + strcmp(type, "prflx") == 0 ? + FARSIGHT_CANDIDATE_TYPE_DERIVED : + FARSIGHT_CANDIDATE_TYPE_RELAY; + + candidates = g_list_append(candidates, ti); + } + + return candidates; +} + +FarsightStream * +jabber_jingle_session_get_stream(const JingleSession *sess) +{ + return sess->stream; +} + +void +jabber_jingle_session_set_stream(JingleSession *sess, FarsightStream *stream) +{ + sess->stream = stream; +} + +PurpleMedia * +jabber_jingle_session_get_media(const JingleSession *sess) +{ + return sess->media; +} + +void +jabber_jingle_session_set_media(JingleSession *sess, PurpleMedia *media) +{ + sess->media = media; +} + +const char * +jabber_jingle_session_get_remote_jid(const JingleSession *sess) +{ + return sess->remote_jid; +} + +void +jabber_jingle_session_set_remote_jid(JingleSession *sess, + const char *remote_jid) +{ + sess->remote_jid = strdup(remote_jid); +} + +const char * +jabber_jingle_session_get_initiator(const JingleSession *sess) +{ + return sess->initiator; +} + +void +jabber_jingle_session_set_initiator(JingleSession *sess, + const char *initiator) +{ + sess->initiator = g_strdup(initiator); +} + +gboolean +jabber_jingle_session_is_initiator(const JingleSession *sess) +{ + return sess->is_initiator; +} + +void +jabber_jingle_session_add_remote_candidate(JingleSession *sess, + const GList *candidate) +{ + /* the length of the candidate list should be 1... */ + GList *cand = candidate; + for (; cand ; cand = cand->next) { + purple_debug_info("jingle", "Adding remote candidate with id = %s\n", + ((FarsightTransportInfo *) cand->data)->candidate_id); + sess->remote_candidates = g_list_append(sess->remote_candidates, + cand->data); + } +} + +static GList * +jabber_jingle_session_get_remote_candidate(const JingleSession *sess, + const gchar *id) +{ + GList *candidates = NULL; + GList *find = sess->remote_candidates; + for (; find ; find = find->next) { + const FarsightTransportInfo *candidate = + (FarsightTransportInfo *) find->data; + if (!strcmp(candidate->candidate_id, id)) { + candidates = g_list_append(candidates, (void *) candidate); + } + } + return candidates; +} + +static xmlnode * +jabber_jingle_session_create_jingle_element(const JingleSession *sess, + const char *action) +{ + xmlnode *jingle = xmlnode_new("jingle"); + xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle"); + xmlnode_set_attrib(jingle, "action", action); + xmlnode_set_attrib(jingle, "initiator", + jabber_jingle_session_get_initiator(sess)); + xmlnode_set_attrib(jingle, "responder", + jabber_jingle_session_is_initiator(sess) ? + jabber_jingle_session_get_remote_jid(sess) : + jabber_jingle_session_get_initiator(sess)); + xmlnode_set_attrib(jingle, "sid", sess->id); + + return jingle; +} + +xmlnode * +jabber_jingle_session_create_terminate(const JingleSession *sess, + const char *reasoncode, + const char *reasontext) +{ + xmlnode *jingle = + jabber_jingle_session_create_jingle_element(sess, "session-terminate"); + xmlnode_set_attrib(jingle, "resoncode", reasoncode); + if (reasontext) { + xmlnode_set_attrib(jingle, "reasontext", reasontext); + } + xmlnode_set_attrib(jingle, "sid", sess->id); + + return jingle; +} + +static xmlnode * +jabber_jingle_session_create_description(const JingleSession *sess) +{ + GList *codecs = + farsight_stream_get_local_codecs(jabber_jingle_session_get_stream(sess)); + + xmlnode *description = xmlnode_new("description"); + xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp"); + + /* get codecs */ + for (; codecs ; codecs = codecs->next) { + FarsightCodec *codec = (FarsightCodec*)codecs->data; + char id[8], clockrate[10]; + xmlnode *payload = xmlnode_new_child(description, "payload-type"); + + g_snprintf(id, sizeof(id), "%d", codec->id); + g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate); + + xmlnode_set_attrib(payload, "name", codec->encoding_name); + xmlnode_set_attrib(payload, "id", id); + xmlnode_set_attrib(payload, "clockrate", clockrate); + } + + return description; +} + +/* split into two separate methods, one to generate session-accept + (includes codecs) and one to generate transport-info (includes transports + candidates) */ +xmlnode * +jabber_jingle_session_create_session_accept(const JingleSession *sess) +{ + xmlnode *jingle = + jabber_jingle_session_create_jingle_element(sess, "session-accept"); + xmlnode *content = NULL; + xmlnode *description = NULL; + + + content = xmlnode_new_child(jingle, "content"); + xmlnode_set_attrib(content, "creator", "initiator"); + xmlnode_set_attrib(content, "name", "audio-content"); + xmlnode_set_attrib(content, "profile", "RTP/AVP"); + + description = jabber_jingle_session_create_description(sess); + xmlnode_insert_child(jingle, description); + + return jingle; +} + +static guint +jabber_jingle_get_priority(guint type, guint network) +{ + switch (type) { + case FARSIGHT_CANDIDATE_TYPE_LOCAL: + return network == 0 ? 4096 : network == 1 ? 2048 : 1024; + break; + case FARSIGHT_CANDIDATE_TYPE_DERIVED: + return 126; + break; + case FARSIGHT_CANDIDATE_TYPE_RELAY: + return 100; + break; + default: + return 0; /* unknown type, should not happen */ + } +} + +xmlnode * +jabber_jingle_session_create_transport_info(const JingleSession *sess, + gchar *candidate_id) +{ + xmlnode *jingle = + jabber_jingle_session_create_jingle_element(sess, "transport-info"); + xmlnode *content = NULL; + xmlnode *transport = NULL; + GList *candidates = + farsight_stream_get_native_candidate( + jabber_jingle_session_get_stream(sess), candidate_id); + + content = xmlnode_new_child(jingle, "content"); + xmlnode_set_attrib(content, "creator", "initiator"); + xmlnode_set_attrib(content, "name", "audio-content"); + xmlnode_set_attrib(content, "profile", "RTP/AVP"); + + transport = xmlnode_new_child(content, "transport"); + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp"); + + /* get transport candidate */ + for (; candidates ; candidates = candidates->next) { + FarsightTransportInfo *transport_info = + (FarsightTransportInfo *) candidates->data; + char port[8]; + char prio[8]; + char component[8]; + xmlnode *candidate = NULL; + + if (!strcmp(transport_info->ip, "127.0.0.1")) { + continue; + } + + candidate = xmlnode_new_child(transport, "candidate"); + + g_snprintf(port, sizeof(port), "%d", transport_info->port); + g_snprintf(prio, sizeof(prio), "%d", + jabber_jingle_get_priority(transport_info->type, 0)); + g_snprintf(component, sizeof(component), "%d", transport_info->component); + + xmlnode_set_attrib(candidate, "component", component); + xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */ + xmlnode_set_attrib(candidate, "generation", "0"); /* ? */ + xmlnode_set_attrib(candidate, "ip", transport_info->ip); + xmlnode_set_attrib(candidate, "network", "0"); /* ? */ + xmlnode_set_attrib(candidate, "port", port); + xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */ + xmlnode_set_attrib(candidate, "protocol", + transport_info->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ? + "udp" : "tcp"); + if (transport_info->username) + xmlnode_set_attrib(candidate, "ufrag", transport_info->username); + if (transport_info->password) + xmlnode_set_attrib(candidate, "pwd", transport_info->password); + + xmlnode_set_attrib(candidate, "type", + transport_info->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? + "host" : + transport_info->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? + "prflx" : + transport_info->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? + "relay" : NULL); + + } + farsight_transport_list_destroy(candidates); + + return jingle; +} + +xmlnode * +jabber_jingle_session_create_content_replace(const JingleSession *sess, + gchar *native_candidate_id, + gchar *remote_candidate_id) +{ + xmlnode *jingle = + jabber_jingle_session_create_jingle_element(sess, "content-replace"); + xmlnode *content = NULL; + xmlnode *transport = NULL; + xmlnode *candidate = NULL; + xmlnode *description = NULL; + xmlnode *payload_type = NULL; + char local_port[8]; + char remote_port[8]; + char prio[8]; + char component[8]; + + purple_debug_info("jingle", "creating content-modify for native candidate %s " \ + ", remote candidate %s\n", native_candidate_id, + remote_candidate_id); + + /* It seems the ID's we get from the Farsight callback is phony... */ + /* + GList *native_candidates = + farsight_stream_get_native_candidate(jabber_jingle_session_get_stream(sess), + native_candidate_id); + purple_debug_info("jingle", "found %d native candidates with id %s\n", + g_list_length(native_candidates), native_candidate_id); + GList *remote_candidates = + jabber_jingle_session_get_remote_candidate(sess, remote_candidate_id); + */ + + /* we assume lists have length == 1, I think they must... */ + /* + FarsightTransportInfo *native = + (FarsightTransportInfo *) native_candidates->data; + FarsightTransportInfo *remote = + (FarsightTransportInfo *) remote_candidates->data; + */ + + content = xmlnode_new_child(jingle, "content"); + xmlnode_set_attrib(content, "creator", "initiator"); + xmlnode_set_attrib(content, "name", "audio-content"); + xmlnode_set_attrib(content, "profile", "RTP/AVP"); + + description = xmlnode_new_child(content, "description"); + xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp"); + + payload_type = xmlnode_new_child(description, "payload-type"); + /* get top codec from codec_intersection to put here... */ + /* later on this should probably handle changing codec */ + + /* Skip creating the actual "content" element for now, since we don't + get enough info in the Farsight callback */ +#if 0 + transport = xmlnode_new_child(content, "transport"); + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp"); + + candidate = xmlnode_new_child(transport, "candidate"); + + g_snprintf(local_port, sizeof(local_port), "%d", native->port); + g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port); + g_snprintf(prio, sizeof(prio), "%d", + jabber_jingle_get_priority(native->type, 0)); + g_snprintf(component, sizeof(component), "%d", native->component); + + xmlnode_set_attrib(candidate, "component", component); + xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */ + xmlnode_set_attrib(candidate, "generation", "0"); /* ? */ + xmlnode_set_attrib(candidate, "ip", native->ip); + xmlnode_set_attrib(candidate, "network", "1"); /* ? */ + xmlnode_set_attrib(candidate, "port", local_port); + xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */ + xmlnode_set_attrib(candidate, "protocol", + native->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ? + "udp" : "tcp"); + if (native->username) + xmlnode_set_attrib(candidate, "ufrag", native->username); + if (native->password) + xmlnode_set_attrib(candidate, "pwd", native->password); + + xmlnode_set_attrib(candidate, "type", + native->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? + "host" : + native->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? + "prflx" : + native->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? + "relay" : NULL); + /* relay */ + if (native->type == FARSIGHT_CANDIDATE_TYPE_RELAY) { + /* set rel-addr and rel-port? How? */ + } + + xmlnode_set_attrib(candidate, "rem-addr", remote->ip); + xmlnode_set_attrib(candidate, "rem-port", remote_port); + + farsight_transport_list_destroy(native_candidates); + farsight_transport_list_destroy(remote_candidates); + +#endif /* 0 */ + + purple_debug_info("jingle", "End create content modify\n"); + + return jingle; +} + +xmlnode * +jabber_jingle_session_create_content_accept(const JingleSession *sess) +{ + xmlnode *jingle = + jabber_jingle_session_create_jingle_element(sess, "content-accept"); + + xmlnode *content = xmlnode_new_child(jingle, "content"); + xmlnode *description = jabber_jingle_session_create_description(sess); + + xmlnode_set_attrib(content, "creator", "initiator"); + xmlnode_set_attrib(content, "name", "audio-content"); + xmlnode_set_attrib(content, "profile", "RTP/AVP"); + + xmlnode_insert_child(jingle, description); + + return jingle; +} + +void +jabber_jingle_session_set_remote_codecs(JingleSession *sess, GList *codecs) +{ + if (sess->remote_codecs) { + farsight_codec_list_destroy(sess->remote_codecs); + } + sess->remote_codecs = codecs; +} + +GList * +jabber_jingle_session_get_remote_codecs(const JingleSession *sess) +{ + return sess->remote_codecs; +} + +#endif /* USE_FARSIGHT */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/jingle.h Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,106 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA + */ + +#ifndef JINGLE_H +#define JINGLE_H + +#include "config.h" +#include "jabber.h" +#include "media.h" + +#include <glib.h> +#include <glib-object.h> + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> + +G_BEGIN_DECLS + +typedef struct { + char *id; + JabberStream *js; + FarsightStream *stream; + PurpleMedia *media; + char *remote_jid; + char *initiator; + gboolean is_initiator; + GList *remote_candidates; + GList *remote_codecs; +} JingleSession; + +JingleSession *jabber_jingle_session_create(JabberStream *js); +JingleSession *jabber_jingle_session_create_by_id(JabberStream *js, + const char *id); + +const char *jabber_jingle_session_get_id(const JingleSession *sess); +JabberStream *jabber_jingle_session_get_js(const JingleSession *sess); + +void jabber_jingle_session_destroy(JingleSession *sess); + +JingleSession *jabber_jingle_session_find_by_id(const char *id); + +FarsightStream *jabber_jingle_session_get_stream(const JingleSession *sess); +void jabber_jingle_session_set_stream(JingleSession *sess, FarsightStream *stream); + +PurpleMedia *jabber_jingle_session_get_media(const JingleSession *sess); +void jabber_jingle_session_set_media(JingleSession *sess, PurpleMedia *media); + +const char *jabber_jingle_session_get_remote_jid(const JingleSession *sess); + +gboolean jabber_jingle_session_is_initiator(const JingleSession *sess); + +void jabber_jingle_session_set_remote_jid(JingleSession *sess, + const char *remote_jid); + +const char *jabber_jingle_session_get_initiator(const JingleSession *sess); +void jabber_jingle_session_set_initiator(JingleSession *sess, + const char *initiator); + +void jabber_jingle_session_add_remote_candidate(JingleSession *sess, + const GList *candidate); + +xmlnode *jabber_jingle_session_create_terminate(const JingleSession *sess, + const char *reasoncode, + const char *reasontext); + +xmlnode *jabber_jingle_session_create_session_accept(const JingleSession *sess); +xmlnode *jabber_jingle_session_create_transport_info(const JingleSession *sess, + gchar *candidate_id); +xmlnode *jabber_jingle_session_create_content_replace(const JingleSession *sess, + gchar *native_candidate_id, + gchar *remote_candidate_id); +xmlnode *jabber_jingle_session_create_content_accept(const JingleSession *sess); + +/** + * Gets a list of Farsight codecs from a Jingle <description> tag + * + * @param description + * @return A GList of FarsightCodecS +*/ +GList *jabber_jingle_get_codecs(const xmlnode *description); + +GList *jabber_jingle_get_candidates(const xmlnode *transport); + +void jabber_jingle_session_set_remote_codecs(JingleSession *sess, + GList *codecs); +GList *jabber_jingle_session_get_remote_codecs(const JingleSession *sess); + +G_END_DECLS + +#endif /* USE_FARSIGHT */ + +#endif /* JINGLE_H */
--- a/libpurple/protocols/jabber/libxmpp.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Tue May 27 06:27:46 2008 +0000 @@ -115,9 +115,10 @@ jabber_unregister_account, /* unregister_user */ jabber_send_attention, /* send_attention */ jabber_attention_types, /* attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + jabber_initiate_media, /* initiate_media */ + jabber_can_do_media /* can_do_media */ }; static gboolean load_plugin(PurplePlugin *plugin) @@ -268,6 +269,9 @@ jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled); jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata); +#ifdef USE_FARSIGHT + jabber_add_feature("voice-v1", "http://www.xmpp.org/extensions/xep-0167.html#ns", NULL); +#endif }
--- a/libpurple/protocols/jabber/presence.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/jabber/presence.c Tue May 27 06:27:46 2008 +0000 @@ -265,6 +265,10 @@ xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); xmlnode_set_attrib(c, "node", CAPS0115_NODE); xmlnode_set_attrib(c, "ver", VERSION); +#ifdef USE_FARSIGHT + /* Make sure this is 'voice-v1', or you won't be able to talk to Google Talk */ + xmlnode_set_attrib(c, "ext", "voice-v1"); +#endif if(js != NULL) { /* add the extensions */
--- a/libpurple/protocols/msn/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/msn/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -95,4 +95,7 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS)
--- a/libpurple/protocols/msn/msn.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/msn/msn.c Tue May 27 06:27:46 2008 +0000 @@ -2435,9 +2435,10 @@ NULL, /* unregister_user */ msn_send_attention, /* send_attention */ msn_attention_types, /* attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ msn_get_account_text_table, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = @@ -2519,3 +2520,4 @@ } PURPLE_INIT_PLUGIN(msn, init_plugin, info); +
--- a/libpurple/protocols/msnp9/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/msnp9/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -87,4 +87,8 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/msnp9/msn.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/msnp9/msn.c Tue May 27 06:27:46 2008 +0000 @@ -2276,9 +2276,10 @@ NULL, /* unregister_user */ msn_send_attention, /* send_attention */ msn_attention_types, /* attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ msn_get_account_text_table, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = @@ -2359,3 +2360,4 @@ } PURPLE_INIT_PLUGIN(msnp9, init_plugin, info); +
--- a/libpurple/protocols/myspace/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/myspace/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -40,4 +40,7 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS)
--- a/libpurple/protocols/myspace/myspace.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/myspace/myspace.c Tue May 27 06:27:46 2008 +0000 @@ -3140,9 +3140,10 @@ NULL, /* unregister_user */ msim_send_attention, /* send_attention */ msim_attention_types, /* attention_types */ - - sizeof(PurplePluginProtocolInfo), /* struct_size */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ msim_get_account_text_table, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ };
--- a/libpurple/protocols/novell/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/novell/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -54,4 +54,8 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/novell/novell.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/novell/novell.c Tue May 27 06:27:46 2008 +0000 @@ -3512,13 +3512,13 @@ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = { @@ -3573,3 +3573,4 @@ } PURPLE_INIT_PLUGIN(novell, init_plugin, info); +
--- a/libpurple/protocols/null/nullprpl.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/null/nullprpl.c Tue May 27 06:27:46 2008 +0000 @@ -1122,10 +1122,12 @@ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - NULL, /* padding... */ - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static void nullprpl_init(PurplePlugin *plugin)
--- a/libpurple/protocols/oscar/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/oscar/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -76,4 +76,8 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/oscar/libaim.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/oscar/libaim.c Tue May 27 06:27:46 2008 +0000 @@ -95,9 +95,10 @@ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* get_attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/libicq.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/oscar/libicq.c Tue May 27 06:27:46 2008 +0000 @@ -95,9 +95,10 @@ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* get_attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/qq/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -94,4 +94,8 @@ -I$(top_builddir)/libpurple \ -DQQ_BUDDY_ICON_DIR=\"$(datadir)/pixmaps/purple/buddy_icons/qq\" \ $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/qq/qq.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Tue May 27 06:27:46 2008 +0000 @@ -701,13 +701,13 @@ NULL, /* PurpleWhiteboardPrplOps */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = {
--- a/libpurple/protocols/sametime/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/sametime/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -32,7 +32,7 @@ AM_CFLAGS = \ - $(GLIB_CFLAGS) $(MEANWHILE_CFLAGS) \ + $(GLIB_CFLAGS) $(MEANWHILE_CFLAGS) $(FARSIGHT_CFLAGS) \ $(DEBUG_CFLAGS) \ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple
--- a/libpurple/protocols/sametime/sametime.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/sametime/sametime.c Tue May 27 06:27:46 2008 +0000 @@ -5188,7 +5188,8 @@ .new_xfer = mw_prpl_new_xfer, .offline_message = NULL, .whiteboard_prpl_ops = NULL, - .send_raw = NULL + .send_raw = NULL, + .struct_size = sizeof(PurplePluginProtocolInfo) };
--- a/libpurple/protocols/silc/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/silc/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -25,7 +25,7 @@ noinst_LIBRARIES = libsilcpurple_la_SOURCES = $(SILCSOURCES) -libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS) +libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS) $(FARSIGHT_LIBS) endif @@ -33,4 +33,8 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/silc/silc.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/silc/silc.c Tue May 27 06:27:46 2008 +0000 @@ -2066,13 +2066,13 @@ &silcpurple_wb_ops, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = @@ -2206,3 +2206,4 @@ } PURPLE_INIT_PLUGIN(silc, init_plugin, info); +
--- a/libpurple/protocols/silc10/silc.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/silc10/silc.c Tue May 27 06:27:46 2008 +0000 @@ -1799,12 +1799,13 @@ &silcpurple_wb_ops, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/simple/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/simple/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -33,4 +33,8 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/simple/simple.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/simple/simple.c Tue May 27 06:27:46 2008 +0000 @@ -2063,13 +2063,13 @@ NULL, /* whiteboard_prpl_ops */ simple_send_raw, /* send_raw */ NULL, /* roomlist_room_serialize */ - - /* padding */ - NULL, - NULL, - NULL, + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; @@ -2135,3 +2135,4 @@ } PURPLE_INIT_PLUGIN(simple, _init_plugin, info); +
--- a/libpurple/protocols/yahoo/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/yahoo/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -53,4 +53,8 @@ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(FARSIGHT_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/yahoo/yahoo.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Tue May 27 06:27:46 2008 +0000 @@ -4379,12 +4379,12 @@ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ NULL, /* unregister_user */ - yahoo_send_attention, yahoo_attention_types, - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = @@ -4477,3 +4477,4 @@ } PURPLE_INIT_PLUGIN(yahoo, init_plugin, info); +
--- a/libpurple/protocols/zephyr/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/zephyr/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -106,5 +106,9 @@ -I$(top_srcdir)/libpurple/protocols \ -DCONFDIR=\"$(sysconfdir)\" \ $(GLIB_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(KRB4_CFLAGS) \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(LIBXML_CFLAGS) +
--- a/libpurple/protocols/zephyr/zephyr.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Tue May 27 06:27:46 2008 +0000 @@ -2915,7 +2915,9 @@ NULL, NULL, sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL + sizeof(PurplePluginProtocolInfo), + NULL, /* initate_media */ + NULL /* can_do_media */ }; static PurplePluginInfo info = {
--- a/libpurple/prpl.h Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/prpl.h Tue May 27 06:27:46 2008 +0000 @@ -64,6 +64,7 @@ #include "conversation.h" #include "ft.h" #include "imgstore.h" +#include "media.h" #include "notify.h" #include "proxy.h" #include "plugin.h" @@ -71,8 +72,8 @@ #include "status.h" #include "whiteboard.h" +/** @copydoc PurpleBuddyIconSpec */ -/** @copydoc PurpleBuddyIconSpec */ struct _PurpleBuddyIconSpec { /** This is a comma-delimited list of image formats or @c NULL if icons * are not supported. Neither the core nor the prpl will actually @@ -400,7 +401,7 @@ * reasons. */ void (*unregister_user)(PurpleAccount *, PurpleAccountUnregistrationCb cb, void *user_data); - + /* Attention API for sending & receiving zaps/nudges/buzzes etc. */ gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type); GList *(*get_attention_types)(PurpleAccount *acct); @@ -436,6 +437,24 @@ * destroyed by the caller when it's no longer needed. */ GHashTable *(*get_account_text_table)(PurpleAccount *account); + +#ifdef USE_FARSIGHT + /** Initiate media with the given buddy */ + PurpleMedia *(*initiate_media)(PurpleConnection *conn, const char *who, PurpleMediaStreamType type); + + gboolean (*can_do_media)(PurpleConnection *conn, const char *who, PurpleMediaStreamType type); + + /* + gboolean (*can_receive_video)(PurpleConnection *conn, const char *who); + gboolean (*can_send_video)(PurpleConnection *conn, const char *who); + gboolean (*can_receive_audio)(PurpleConnection *conn, const char *who); + gboolean (*can_send_audio)(PurpleConnection *conn, const char *who); + */ + +#else + void (*initiate_media)(void); + void (*can_do_media)(void); +#endif }; #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \ @@ -710,3 +729,4 @@ #endif #endif /* _PRPL_H_ */ +
--- a/libpurple/server.c Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/server.c Tue May 27 06:27:46 2008 +0000 @@ -37,6 +37,9 @@ #include "server.h" #include "status.h" #include "util.h" +#ifdef USE_FARSIGHT +#include "media.h" +#endif #define SECS_BEFORE_RESENDING_AUTORESPONSE 600 #define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married" @@ -1052,3 +1055,45 @@ } } } + +#ifdef USE_FARSIGHT +PurpleMedia *serv_initiate_media(PurpleConnection *gc, const char *who, + PurpleMediaStreamType type) +{ + PurplePlugin *prpl = NULL; + PurplePluginProtocolInfo *prpl_info = NULL; + + if (gc) + prpl = purple_connection_get_prpl(gc); + if (prpl) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info && prpl_info->initiate_media) { + /* should check that the protol supports this media type here.... */ + return prpl_info->initiate_media(gc, who, type); + } else { + return NULL; + } +} + +gboolean +serv_can_do_media(PurpleConnection *gc, const char *who, + PurpleMediaStreamType type) +{ + PurplePlugin *prpl = NULL; + PurplePluginProtocolInfo *prpl_info = NULL; + + if (gc) + prpl = purple_connection_get_prpl(gc); + if (prpl) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info && prpl_info->can_do_media) { + /* should check that the protol supports this media type here.... */ + return prpl_info->can_do_media(gc, who, type); + } else { + return FALSE; + } +} + +#endif
--- a/libpurple/server.h Tue May 27 04:11:04 2008 +0000 +++ b/libpurple/server.h Tue May 27 06:27:46 2008 +0000 @@ -29,6 +29,7 @@ #include "account.h" #include "conversation.h" #include "prpl.h" +#include "media.h" #ifdef __cplusplus extern "C" { @@ -182,6 +183,17 @@ PurpleMessageFlags flags, const char *message, time_t mtime); void serv_send_file(PurpleConnection *gc, const char *who, const char *file); +#ifdef USE_FARSIGHT +PurpleMedia *serv_initiate_media(PurpleConnection *gc, const char *who, + PurpleMediaStreamType type); +gboolean serv_can_do_media(PurpleConnection *gc, const char *who, + PurpleMediaStreamType type); +#else +/* hmm, is this really nice? */ +void *serv_initiate_media(void*, void*, void*); +void *serv_can_do_media(void *, void *, void *); +#endif + #ifdef __cplusplus } #endif
--- a/pidgin/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -99,6 +99,7 @@ gtkimhtmltoolbar.c \ gtklog.c \ gtkmain.c \ + gtkmedia.c \ gtkmenutray.c \ gtknotify.c \ gtkplugin.c \ @@ -151,6 +152,7 @@ gtkimhtml.h \ gtkimhtmltoolbar.h \ gtklog.h \ + gtkmedia.c \ gtkmenutray.h \ gtknickcolors.h \ gtknotify.h \ @@ -196,6 +198,8 @@ $(STARTUP_NOTIFICATION_LIBS) \ $(LIBXML_LIBS) \ $(GTK_LIBS) \ + $(FARSIGHT_LIBS) \ + $(GSTPROPS_LIBS) \ $(top_builddir)/libpurple/libpurple.la if USE_INTERNAL_LIBGADU @@ -220,5 +224,7 @@ $(GTKSPELL_CFLAGS) \ $(STARTUP_NOTIFICATION_CFLAGS) \ $(LIBXML_CFLAGS) \ - $(INTGG_CFLAGS) + $(INTGG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTPROPS_CFLAGS) endif # ENABLE_GTK
--- a/pidgin/gtkconv.c Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkconv.c Tue May 27 06:27:46 2008 +0000 @@ -46,6 +46,7 @@ #include "idle.h" #include "imgstore.h" #include "log.h" +#include "mediamanager.h" #include "notify.h" #include "prpl.h" #include "request.h" @@ -60,6 +61,7 @@ #include "gtkimhtml.h" #include "gtkimhtmltoolbar.h" #include "gtklog.h" +#include "gtkmedia.h" #include "gtkmenutray.h" #include "gtkpounce.h" #include "gtkprefs.h" @@ -1194,6 +1196,14 @@ gtk_widget_grab_focus(s->entry); } +#ifdef USE_FARSIGHT +/* Forward declare this here, because I want to keep VV-related stuff together +for now */ +static void +menu_initiate_voice_call_cb(gpointer data, guint action, GtkWidget *widget); + +#endif + static void menu_send_file_cb(gpointer data, guint action, GtkWidget *widget) { @@ -3053,6 +3063,11 @@ { "/Conversation/sep1", NULL, NULL, 0, "<Separator>", NULL }, +#ifdef USE_FARSIGHT + { N_("/Conversation/_Voice Call..."), NULL, menu_initiate_voice_call_cb, 0, + "<StockItem>", PIDGIN_STOCK_TOOLBAR_CALL}, +#endif + { N_("/Conversation/Se_nd File..."), NULL, menu_send_file_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_FILE }, { N_("/Conversation/Add Buddy _Pounce..."), NULL, menu_add_pounce_cb, 0, "<Item>", NULL }, @@ -3362,6 +3377,12 @@ gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/View Log")); +#ifdef USE_FARSIGHT + win->menu.call = + gtk_item_factory_get_widget(win->menu.item_factory, + N_("/Conversation/Voice Call...")); +#endif + /* --- */ win->menu.send_file = @@ -4716,7 +4737,7 @@ int buddyicon_size = 0; /* Setup the top part of the pane */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + gtkconv->topvbox = vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_widget_show(vbox); /* Setup the info pane */ @@ -6321,6 +6342,28 @@ else buttons &= ~GTK_IMHTML_CUSTOM_SMILEY; +#ifdef USE_FARSIGHT + /* check if account support voice calls, and if the current buddy + supports it */ + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + if (serv_can_do_media(gc, purple_conversation_get_name(conv), + PURPLE_MEDIA_RECV_AUDIO & PURPLE_MEDIA_SEND_AUDIO)) { + buttons |= GTK_IMHTML_CALL; + gtk_widget_set_sensitive(win->menu.call, TRUE); + } else { + buttons &= ~GTK_IMHTML_CALL; + gtk_widget_set_sensitive(win->menu.call, FALSE); + } + } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + /* for now, don't care about chats... */ + buttons &= ~GTK_IMHTML_CALL; + gtk_widget_set_sensitive(win->menu.call, FALSE); + } else { + buttons &= ~GTK_IMHTML_CALL; + gtk_widget_set_sensitive(win->menu.call, FALSE); + } +#endif + gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons); if (account != NULL) gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account)); @@ -6855,7 +6898,7 @@ gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE); #endif gtk_widget_add_events(event, - GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); + GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); g_signal_connect(G_OBJECT(event), "button-press-event", G_CALLBACK(icon_menu), gtkconv); @@ -7588,6 +7631,64 @@ return TRUE; } + +#ifdef USE_FARSIGHT + +static void +pidgin_gtkmedia_message_cb(PidginMedia *media, const char *msg, PurpleConversation *conv) +{ + purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); +} + +static void +menu_initiate_voice_call_cb(gpointer data, guint action, GtkWidget *widget) +{ + PidginWindow *win = (PidginWindow *)data; + PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win); + PurpleConnection *gc = purple_conversation_get_gc(conv); + + PurpleMedia *media = + serv_initiate_media(gc, + purple_conversation_get_name(conv), + PURPLE_MEDIA_RECV_AUDIO & PURPLE_MEDIA_SEND_AUDIO); + + purple_media_wait(media); +} + +static void +pidgin_conv_new_media_cb(PurpleMediaManager *manager, PurpleMedia *media, gpointer nul) +{ + GstElement *sendbin, *sendlevel; + GstElement *recvbin, *recvlevel; + + GtkWidget *gtkmedia; + PurpleConversation *conv; + PidginConversation *gtkconv; + + purple_media_audio_init_src(&sendbin, &sendlevel); + purple_media_audio_init_recv(&recvbin, &recvlevel); + + purple_media_set_audio_src(media, sendbin); + purple_media_set_audio_sink(media, recvbin); + + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, + purple_connection_get_account(purple_media_get_connection(media)), + purple_media_get_screenname(media)); + gtkconv = PIDGIN_CONVERSATION(conv); + if (gtkconv->gtkmedia) + gtk_widget_destroy(gtkconv->gtkmedia); + + gtkmedia = pidgin_media_new(media, sendlevel, recvlevel); + gtk_box_pack_start(GTK_BOX(gtkconv->topvbox), gtkmedia, FALSE, FALSE, 0); + gtk_widget_show(gtkmedia); + g_signal_connect(G_OBJECT(gtkmedia), "message", G_CALLBACK(pidgin_gtkmedia_message_cb), conv); + + gtkconv->gtkmedia = gtkmedia; + g_signal_connect(G_OBJECT(gtkmedia), "destroy", G_CALLBACK(gtk_widget_destroyed), &(gtkconv->gtkmedia)); +} + +#endif + void * pidgin_conversations_get_handle(void) { @@ -7689,6 +7790,8 @@ purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/hide_new", hide_new_pref_cb, NULL); + g_signal_connect(G_OBJECT(purple_media_manager_get()), "init-media", + G_CALLBACK(pidgin_conv_new_media_cb), NULL); /**********************************************************************
--- a/pidgin/gtkconv.h Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkconv.h Tue May 27 06:27:46 2008 +0000 @@ -169,6 +169,8 @@ GtkWidget *infopane; GtkListStore *infopane_model; GtkTreeIter infopane_iter; + GtkWidget *topvbox; + GtkWidget *gtkmedia; /* Used when attaching a PidginConversation to a PurpleConversation * with message history */
--- a/pidgin/gtkconvwin.h Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkconvwin.h Tue May 27 06:27:46 2008 +0000 @@ -50,6 +50,7 @@ GtkWidget *view_log; + GtkWidget *call; GtkWidget *send_file; GtkWidget *add_pounce; GtkWidget *get_info;
--- a/pidgin/gtkdebug.c Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkdebug.c Tue May 27 06:27:46 2008 +0000 @@ -985,6 +985,11 @@ #ifdef USE_GSTREAMER REGISTER_G_LOG_HANDLER("GStreamer"); #endif +#ifdef USE_FARSIGHT + REGISTER_G_LOG_HANDLER("farsight"); + REGISTER_G_LOG_HANDLER("farsight-transmitter"); + REGISTER_G_LOG_HANDLER("farsight-rtp"); +#endif #ifdef _WIN32 if (!purple_debug_is_enabled())
--- a/pidgin/gtkimhtml.h Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkimhtml.h Tue May 27 06:27:46 2008 +0000 @@ -78,6 +78,7 @@ GTK_IMHTML_STRIKE = 1 << 13, /** Show custom smileys when appropriate. @since 2.5.0 */ GTK_IMHTML_CUSTOM_SMILEY = 1 << 14, + GTK_IMHTML_CALL = 1 << 15, GTK_IMHTML_ALL = -1 } GtkIMHtmlButtons;
--- a/pidgin/gtkimhtmltoolbar.c Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Tue May 27 06:27:46 2008 +0000 @@ -40,6 +40,8 @@ #include "gtkthemes.h" #include "gtkutils.h" +#include "debug.h" + #include <gdk/gdkkeysyms.h> static GtkHBoxClass *parent_class = NULL; @@ -449,12 +451,12 @@ static void insert_hr_cb(GtkWidget *widget, GtkIMHtmlToolbar *toolbar) { - GtkTextIter iter; - GtkTextMark *ins; + GtkTextIter iter; + GtkTextMark *ins; GtkIMHtmlScalable *hr; - ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml))); - gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins); + ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml))); + gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins); hr = gtk_imhtml_hr_new(); gtk_imhtml_hr_add_to(hr, GTK_IMHTML(toolbar->imhtml), &iter); } @@ -905,6 +907,14 @@ gtk_widget_grab_focus(toolbar->imhtml); } +#ifdef USE_FARSIGHT +static void +init_voice_call_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar) +{ + purple_debug_info("gtkimhtmltoolbar", "Call clicked!\n"); +} +#endif + static void update_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar) { gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD); @@ -933,6 +943,9 @@ gtk_widget_set_sensitive(GTK_WIDGET(toolbar->image), buttons & GTK_IMHTML_IMAGE); gtk_widget_set_sensitive(GTK_WIDGET(toolbar->link), buttons & GTK_IMHTML_LINK); gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smiley), buttons & GTK_IMHTML_SMILEY); +#ifdef USE_FARSIGHT + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->call), buttons & GTK_IMHTML_CALL); +#endif } /* we call this when we want to _set_active the toggle button, it'll @@ -1221,6 +1234,10 @@ {PIDGIN_STOCK_TOOLBAR_INSERT_LINK, insert_link_cb, &toolbar->link, _("Insert Link")}, {PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, insert_image_cb, &toolbar->image, _("Insert IM Image")}, {PIDGIN_STOCK_TOOLBAR_SMILEY, insert_smiley_cb, &toolbar->smiley, _("Insert Smiley")}, +#ifdef USE_FARSIGHT + {"", NULL, NULL, NULL}, + {PIDGIN_STOCK_TOOLBAR_CALL, init_voice_call_cb, &toolbar->call, _("Call")}, +#endif {NULL, NULL, NULL, NULL} }; int iter; @@ -1286,6 +1303,11 @@ GtkWidget *insert_button; GtkWidget *font_button; GtkWidget *smiley_button; + +#ifdef USE_FARSIGHT + GtkWidget *call_button; +#endif /* USE_FARSIGHT */ + GtkWidget *font_menu; GtkWidget *insert_menu; GtkWidget *menuitem; @@ -1427,6 +1449,28 @@ g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley); gtk_widget_show_all(smiley_button); +#ifdef USE_FARSIGHT + /* Sep */ + sep = gtk_vseparator_new(); + gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0); + gtk_widget_show_all(sep); + + /* Call */ + call_button = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(call_button), GTK_RELIEF_NONE); + bbox = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(call_button), bbox); + image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_CALL, + gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); + gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0); + label = gtk_label_new_with_mnemonic(_("Call")); + gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), call_button, FALSE, FALSE, 0); + g_signal_connect_swapped(G_OBJECT(call_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->call); + gtk_widget_show_all(call_button); + +#endif /* USE_FARSIGHT */ + gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0); g_object_set_data(G_OBJECT(hbox), "lean-view", box); gtk_widget_show(box);
--- a/pidgin/gtkimhtmltoolbar.h Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkimhtmltoolbar.h Tue May 27 06:27:46 2008 +0000 @@ -76,6 +76,7 @@ char *sml; GtkWidget *strikethrough; GtkWidget *insert_hr; + GtkWidget *call; }; struct _GtkIMHtmlToolbarClass {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkmedia.c Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,398 @@ +/** + * @file media.c Account API + * @ingroup core + * + * Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 "internal.h" +#include "connection.h" +#include "media.h" + +#include "gtkmedia.h" + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> + +typedef enum +{ + /* Waiting for response */ + PIDGIN_MEDIA_WAITING = 1, + /* Got request */ + PIDGIN_MEDIA_REQUESTED, + /* Accepted call */ + PIDGIN_MEDIA_ACCEPTED, + /* Rejected call */ + PIDGIN_MEDIA_REJECTED, +} PidginMediaState; + +struct _PidginMediaPrivate +{ + PurpleMedia *media; + GstElement *send_level; + GstElement *recv_level; + + GtkWidget *calling; + GtkWidget *accept; + GtkWidget *reject; + GtkWidget *hangup; + + GtkWidget *send_progress; + GtkWidget *recv_progress; + + PidginMediaState state; +}; + +#define PIDGIN_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_MEDIA, PidginMediaPrivate)) + +static void pidgin_media_class_init (PidginMediaClass *klass); +static void pidgin_media_init (PidginMedia *media); +static void pidgin_media_finalize (GObject *object); +static void pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state); + +static GtkHBoxClass *parent_class = NULL; + + + +enum { + MESSAGE, + LAST_SIGNAL +}; +static guint pidgin_media_signals[LAST_SIGNAL] = {0}; + +enum { + PROP_0, + PROP_MEDIA, + PROP_SEND_LEVEL, + PROP_RECV_LEVEL +}; + +GType +pidgin_media_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(PidginMediaClass), + NULL, + NULL, + (GClassInitFunc) pidgin_media_class_init, + NULL, + NULL, + sizeof(PidginMedia), + 0, + (GInstanceInitFunc) pidgin_media_init, + NULL + }; + type = g_type_register_static(GTK_TYPE_HBOX, "PidginMedia", &info, 0); + } + return type; +} + + +static void +pidgin_media_class_init (PidginMediaClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + GtkContainerClass *container_class = (GtkContainerClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = pidgin_media_finalize; + gobject_class->set_property = pidgin_media_set_property; + gobject_class->get_property = pidgin_media_get_property; + + g_object_class_install_property(gobject_class, PROP_MEDIA, + g_param_spec_object("media", + "PurpleMedia", + "The PurpleMedia associated with this media.", + PURPLE_TYPE_MEDIA, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, PROP_SEND_LEVEL, + g_param_spec_object("send-level", + "Send level", + "The GstElement of this media's send 'level'", + GST_TYPE_ELEMENT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, PROP_RECV_LEVEL, + g_param_spec_object("recv-level", + "Receive level", + "The GstElement of this media's recv 'level'", + GST_TYPE_ELEMENT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + pidgin_media_signals[MESSAGE] = g_signal_new("message", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + g_type_class_add_private(klass, sizeof(PidginMediaPrivate)); +} + + +static void +pidgin_media_init (PidginMedia *media) +{ + media->priv = PIDGIN_MEDIA_GET_PRIVATE(media); + media->priv->calling = gtk_label_new_with_mnemonic("Calling..."); + media->priv->hangup = gtk_button_new_with_label("Hangup"); + media->priv->accept = gtk_button_new_with_label("Accept"); + media->priv->reject = gtk_button_new_with_label("Reject"); + media->priv->send_progress = gtk_progress_bar_new(); + media->priv->recv_progress = gtk_progress_bar_new(); + + gtk_widget_set_size_request(media->priv->send_progress, 70, 5); + gtk_widget_set_size_request(media->priv->recv_progress, 70, 5); + + gtk_box_pack_start(GTK_BOX(media), media->priv->calling, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(media), media->priv->hangup, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(media), media->priv->accept, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(media), media->priv->reject, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(media), media->priv->send_progress, FALSE, FALSE, 6); + gtk_box_pack_start(GTK_BOX(media), media->priv->recv_progress, FALSE, FALSE, 6); + + gtk_widget_show(media->priv->send_progress); + gtk_widget_show(media->priv->recv_progress); + + gtk_widget_show_all(media->priv->accept); + gtk_widget_show_all(media->priv->reject); +} + +static void +pidgin_media_finalize (GObject *media) +{ + PidginMedia *gtkmedia = PIDGIN_MEDIA(media); + if (gtkmedia->priv->media) + g_object_unref(gtkmedia->priv->media); + if (gtkmedia->priv->send_level) + gst_object_unref(gtkmedia->priv->send_level); + if (gtkmedia->priv->recv_level) + gst_object_unref(gtkmedia->priv->recv_level); +} + +static void +pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg) +{ + g_signal_emit(gtkmedia, pidgin_media_signals[MESSAGE], 0, msg); +} + +static gboolean +level_message_cb(GstBus *bus, GstMessage *message, PidginMedia *gtkmedia) +{ + const GstStructure *s; + const gchar *name; + + int channels; + gdouble rms_db, peak_db, decay_db; + gdouble rms; + const GValue *list; + const GValue *value; + + GstElement *src = GST_ELEMENT(message); + + if (message->type != GST_MESSAGE_ELEMENT) + return TRUE; + + s = gst_message_get_structure(message); + name = gst_structure_get_name(s); + + if (strcmp(name, "level")) + return TRUE; + + list = gst_structure_get_value(s, "rms"); + + /* Only bother with the first channel. */ + value = gst_value_list_get_value(list, 0); + rms_db = g_value_get_double(value); + + if (!strcmp(gst_element_get_name(src), "sendlevel")) + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->send_progress), pow(10, rms_db / 20) * 5); + else + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->recv_progress), pow(10, rms_db / 20) * 5); + + return TRUE; +} + +static void +pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia) +{ + GstElement *element = purple_media_get_audio_pipeline(media); + gst_bus_add_signal_watch(GST_BUS(gst_pipeline_get_bus(GST_PIPELINE(element)))); + g_signal_connect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), "message", G_CALLBACK(level_message_cb), gtkmedia); + printf("\n\nbus: %p\n", gst_pipeline_get_bus(GST_PIPELINE(element))); +} + +static void +pidgin_media_wait_cb(PurpleMedia *media, PidginMedia *gtkmedia) +{ + pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_WAITING); +} + +/* maybe we should have different callbacks for when we received the accept + and we accepted ourselves */ +static void +pidgin_media_accept_cb(PurpleMedia *media, PidginMedia *gtkmedia) +{ + pidgin_media_emit_message(gtkmedia, _("Call in progress.")); + pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED); +} + +static void +pidgin_media_hangup_cb(PurpleMedia *media, PidginMedia *gtkmedia) +{ + pidgin_media_emit_message(gtkmedia, _("You have ended the call.")); + gtk_widget_destroy(GTK_WIDGET(gtkmedia)); +} + +static void +pidgin_media_got_hangup_cb(PurpleMedia *media, PidginMedia *gtkmedia) +{ + pidgin_media_emit_message(gtkmedia, _("The call has been terminated.")); + gtk_widget_destroy(GTK_WIDGET(gtkmedia)); +} + +static void +pidgin_media_reject_cb(PurpleMedia *media, PidginMedia *gtkmedia) +{ + pidgin_media_emit_message(gtkmedia, _("You have rejected the call.")); + gtk_widget_destroy(GTK_WIDGET(gtkmedia)); +} + +static void +pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + PidginMedia *media; + g_return_if_fail(PIDGIN_IS_MEDIA(object)); + + media = PIDGIN_MEDIA(object); + switch (prop_id) { + case PROP_MEDIA: + if (media->priv->media) + g_object_unref(media->priv->media); + media->priv->media = g_value_get_object(value); + g_object_ref(media->priv->media); + g_signal_connect_swapped(G_OBJECT(media->priv->accept), "clicked", + G_CALLBACK(purple_media_accept), media->priv->media); + g_signal_connect_swapped(G_OBJECT(media->priv->reject), "clicked", + G_CALLBACK(purple_media_reject), media->priv->media); + g_signal_connect_swapped(G_OBJECT(media->priv->hangup), "clicked", + G_CALLBACK(purple_media_hangup), media->priv->media); + + g_signal_connect(G_OBJECT(media->priv->media), "accepted", + G_CALLBACK(pidgin_media_accept_cb), media); + g_signal_connect(G_OBJECT(media->priv->media) ,"ready", + G_CALLBACK(pidgin_media_ready_cb), media); + g_signal_connect(G_OBJECT(media->priv->media) ,"wait", + G_CALLBACK(pidgin_media_wait_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "hangup", + G_CALLBACK(pidgin_media_hangup_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "reject", + G_CALLBACK(pidgin_media_reject_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "got-hangup", + G_CALLBACK(pidgin_media_got_hangup_cb), media); + g_signal_connect(G_OBJECT(media->priv->media), "got-accept", + G_CALLBACK(pidgin_media_accept_cb), media); + break; + case PROP_SEND_LEVEL: + if (media->priv->send_level) + gst_object_unref(media->priv->send_level); + media->priv->send_level = g_value_get_object(value); + g_object_ref(media->priv->send_level); + break; + case PROP_RECV_LEVEL: + if (media->priv->recv_level) + gst_object_unref(media->priv->recv_level); + media->priv->recv_level = g_value_get_object(value); + g_object_ref(media->priv->recv_level); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + PidginMedia *media; + g_return_if_fail(PIDGIN_IS_MEDIA(object)); + + media = PIDGIN_MEDIA(object); + + switch (prop_id) { + case PROP_MEDIA: + g_value_set_object(value, media->priv->media); + break; + case PROP_SEND_LEVEL: + g_value_set_object(value, media->priv->send_level); + break; + case PROP_RECV_LEVEL: + g_value_set_object(value, media->priv->recv_level); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +GtkWidget * +pidgin_media_new(PurpleMedia *media, GstElement *sendlevel, GstElement *recvlevel) +{ + PidginMedia *gtkmedia = g_object_new(pidgin_media_get_type(), "media", media, + "send-level", sendlevel, + "recv-level", recvlevel, NULL); + return GTK_WIDGET(gtkmedia); +} + +static void +pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state) +{ + gtkmedia->priv->state = state; + switch (state) { + case PIDGIN_MEDIA_WAITING: + gtk_widget_show(gtkmedia->priv->calling); + gtk_widget_hide(gtkmedia->priv->accept); + gtk_widget_hide(gtkmedia->priv->reject); + gtk_widget_show(gtkmedia->priv->hangup); + break; + case PIDGIN_MEDIA_REQUESTED: + gtk_widget_hide(gtkmedia->priv->calling); + gtk_widget_show(gtkmedia->priv->accept); + gtk_widget_show(gtkmedia->priv->reject); + gtk_widget_hide(gtkmedia->priv->hangup); + break; + case PIDGIN_MEDIA_ACCEPTED: + gtk_widget_show(gtkmedia->priv->hangup); + gtk_widget_hide(gtkmedia->priv->calling); + gtk_widget_hide(gtkmedia->priv->accept); + gtk_widget_hide(gtkmedia->priv->reject); + break; + default: + break; + } +} + +#endif /* USE_FARSIGHT */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkmedia.h Tue May 27 06:27:46 2008 +0000 @@ -0,0 +1,70 @@ +/** + * @file media.h Account API + * @ingroup core + * + * Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 __GTKMEDIA_H_ +#define __GTKMEDIA_H_ + +#ifdef USE_FARSIGHT + +#include <farsight/farsight.h> +#include <gtk/gtk.h> +#include <glib-object.h> + +#include "connection.h" + +G_BEGIN_DECLS + +#define PIDGIN_TYPE_MEDIA (pidgin_media_get_type()) +#define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia)) +#define PIDGIN_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MEDIA, PidginMediaClass)) +#define PIDGIN_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MEDIA)) +#define PIDGIN_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MEDIA)) +#define PIDGIN_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_MEDIA, PidginMediaClass)) + +typedef struct _PidginMedia PidginMedia; +typedef struct _PidginMediaClass PidginMediaClass; +typedef struct _PidginMediaPrivate PidginMediaPrivate; + +struct _PidginMediaClass +{ + GtkHBoxClass parent_class; +}; + +struct _PidginMedia +{ + GtkHBox parent; + PidginMediaPrivate *priv; +}; + +GType pidgin_media_get_type(void); + +GtkWidget *pidgin_media_new(PurpleMedia *media, GstElement *send_level, GstElement *recv_level); + +G_END_DECLS + +#endif /* USE_FARSIGHT */ + + +#endif /* __GTKMEDIA_H_ */
--- a/pidgin/gtkprefs.c Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkprefs.c Tue May 27 06:27:46 2008 +0000 @@ -145,12 +145,10 @@ if (type == PURPLE_PREF_INT) { int_value = GPOINTER_TO_INT(g_object_get_data(w, "value")); - purple_prefs_set_int(key, int_value); } else if (type == PURPLE_PREF_STRING) { str_value = (const char *)g_object_get_data(w, "value"); - purple_prefs_set_string(key, str_value); } else if (type == PURPLE_PREF_BOOLEAN) { @@ -945,7 +943,7 @@ _("Never"), "never", NULL); gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); vbox = pidgin_make_frame(ret, _("Conversation Window Hiding")); label = pidgin_prefs_dropdown(vbox, _("_Hide new IM conversations:"), @@ -955,7 +953,7 @@ _("Always"), "always", NULL); gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); /* All the tab options! */ @@ -990,7 +988,7 @@ #endif NULL); gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); names = pidgin_conv_placement_get_options(); label = pidgin_prefs_dropdown_from_list(vbox2, _("N_ew conversations:"), @@ -2019,6 +2017,105 @@ return ret; } +#ifdef USE_FARSIGHT + +/* get a GList of pairs name / device */ +static GList * +get_device_items(const GstElement *element, + const GList *devices) +{ + GList *ret = NULL; + + for(; devices ; devices = devices->next) { + gchar *name = purple_media_get_device_name(element, devices->data); + ret = g_list_append(ret, name); + ret = g_list_append(ret, name); + } + + return ret; +} + +/* + * Test functions to run video preview + * (this is not really functional right now...) + * + */ +static void +preview_button_clicked(GtkWidget *widget, gpointer *data) +{ + GstElement *video = (GstElement *) data; + + /* create a preview window... */ + GstElement *output = gst_element_factory_make("autovideosink", NULL); + GstElement *pipeline = NULL; + GError *p_err; + + gchar test_pipeline_str[50] = "v4lsrc ! ffmpegcolorspace ! autovideosink"; + pipeline = gst_parse_launch (test_pipeline_str, &p_err); + + gst_element_set_state(pipeline, GST_STATE_PLAYING); +} + +static GtkWidget * +media_page() +{ + GtkWidget *ret; + GtkWidget *sg; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *dd; + GtkWidget *preview_button; + + GstElement *video = purple_media_get_element("v4lsrc"); + GstElement *audio = purple_media_get_element("alsasrc"); + + GList *video_devices = purple_media_get_devices(video); + GList *audio_devices = purple_media_get_devices(audio); + + GList *video_items = get_device_items(video, video_devices); + GList *audio_items = get_device_items(audio, audio_devices); + + g_list_free(video_devices); + g_list_free(audio_devices); + + ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER); + + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + vbox = pidgin_make_frame (ret, _("Video Input")); + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + dd = pidgin_prefs_dropdown_from_list(hbox, _("_Device:"), PURPLE_PREF_STRING, + "/purple/media/video/device", + video_items); + + gtk_size_group_add_widget(sg, dd); + gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5); + + preview_button = gtk_button_new_with_mnemonic(_("_Preview")); + g_signal_connect(G_OBJECT(preview_button), "clicked", + G_CALLBACK(preview_button_clicked), video); + + gtk_container_add(hbox, preview_button); + gtk_container_add(vbox, hbox); + + vbox = pidgin_make_frame (ret, _("Audio Input")); + dd = pidgin_prefs_dropdown_from_list(vbox, _("_Device:"), PURPLE_PREF_STRING, + "/purple/media/audio/device", + audio_items); + + gtk_size_group_add_widget(sg, dd); + gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5); + + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + gtk_widget_show_all(ret); + + return ret; +} + +#endif static void set_idle_away(PurpleSavedStatus *status) @@ -2144,6 +2241,10 @@ prefs_notebook_add_page(_("Conversations"), conv_page(), notebook_page++); prefs_notebook_add_page(_("Smiley Themes"), theme_page(), notebook_page++); prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++); + +#if USE_FARSIGHT + prefs_notebook_add_page(_("Media"), media_page(), notebook_page++); +#endif prefs_notebook_add_page(_("Network"), network_page(), notebook_page++); #ifndef _WIN32 /* We use the registered default browser in windows */ @@ -2269,6 +2370,14 @@ purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/smileys/theme", smiley_theme_pref_cb, NULL); +#ifdef USE_FARSIGHT + purple_prefs_add_none("/purple/media"); + purple_prefs_add_none("/purple/media/video"); + purple_prefs_add_string("/purple/media/video/device", ""); + purple_prefs_add_none("/purple/media/audio"); + purple_prefs_add_string("/purple/media/audio/device", ""); +#endif /* USE_FARSIGHT */ + pidgin_prefs_update_old(); }
--- a/pidgin/gtkprefs.h Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/gtkprefs.h Tue May 27 06:27:46 2008 +0000 @@ -29,6 +29,7 @@ #include "prefs.h" + /** * Initializes all UI-specific preferences. */
--- a/pidgin/pidginstock.c Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/pidginstock.c Tue May 27 06:27:46 2008 +0000 @@ -169,6 +169,10 @@ { PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL }, { PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, +#ifdef USE_FARSIGHT + { PIDGIN_STOCK_TOOLBAR_CALL, "toolbar", "call.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, +#endif + { PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, { PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, { PIDGIN_STOCK_TRAY_AWAY, "tray", "tray-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, @@ -208,39 +212,39 @@ filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL); source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); + gtk_icon_source_set_filename(source, filename); gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR); - gtk_icon_source_set_direction_wildcarded(source, !rtl); + gtk_icon_source_set_direction_wildcarded(source, !rtl); gtk_icon_source_set_size(source, sizeid); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); + gtk_icon_source_set_size_wildcarded(source, FALSE); + gtk_icon_source_set_state_wildcarded(source, TRUE); + gtk_icon_set_add_source(iconset, source); gtk_icon_source_free(source); if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) { source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction_wildcarded(source, TRUE); - gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - gtk_icon_source_free(source); + gtk_icon_source_set_filename(source, filename); + gtk_icon_source_set_direction_wildcarded(source, TRUE); + gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU); + gtk_icon_source_set_size_wildcarded(source, FALSE); + gtk_icon_source_set_state_wildcarded(source, TRUE); + gtk_icon_set_add_source(iconset, source); + gtk_icon_source_free(source); } - g_free(filename); + g_free(filename); - if (rtl) { + if (rtl) { filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL); - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL); - gtk_icon_source_set_size(source, sizeid); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); + source = gtk_icon_source_new(); + gtk_icon_source_set_filename(source, filename); + gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL); + gtk_icon_source_set_size(source, sizeid); + gtk_icon_source_set_size_wildcarded(source, FALSE); + gtk_icon_source_set_state_wildcarded(source, TRUE); + gtk_icon_set_add_source(iconset, source); g_free(filename); gtk_icon_source_free(source); - } + } }
--- a/pidgin/pidginstock.h Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/pidginstock.h Tue May 27 06:27:46 2008 +0000 @@ -129,6 +129,9 @@ #define PIDGIN_STOCK_TOOLBAR_UNBLOCK "pidgin-unblock" #define PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR "pidgin-select-avatar" #define PIDGIN_STOCK_TOOLBAR_SEND_FILE "pidgin-send-file" +#ifdef USE_FARSIGHT +#define PIDGIN_STOCK_TOOLBAR_CALL "pidgin-call" +#endif /* Tray icons */ #define PIDGIN_STOCK_TRAY_AVAILABLE "pidgin-tray-available"
--- a/pidgin/plugins/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/plugins/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -123,6 +123,7 @@ -I$(top_srcdir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GTK_CFLAGS) \ $(PLUGIN_CFLAGS)
--- a/pidgin/plugins/cap/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/plugins/cap/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -15,7 +15,7 @@ endif -cap_la_LIBADD = $(GTK_LIBS) $(SQLITE3_LIBS) +cap_la_LIBADD = $(GTK_LIBS) $(SQLITE3_LIBS) $(FARSIGHT_LIBS) $(GSTPROPS_LIBS) AM_CPPFLAGS = \ -DDATADIR=\"$(datadir)\" \ @@ -24,6 +24,8 @@ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTPROPS_CFLAGS) \ $(SQLITE3_CFLAGS) EXTRA_DIST = Makefile.mingw
--- a/pidgin/plugins/gestures/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/plugins/gestures/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -23,4 +23,5 @@ -I$(top_builddir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GTK_CFLAGS)
--- a/pidgin/plugins/gevolution/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/plugins/gevolution/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -26,4 +26,5 @@ -I$(top_srcdir)/pidgin \ $(EVOLUTION_ADDRESSBOOK_CFLAGS) \ $(DEBUG_CFLAGS) \ - $(GTK_CFLAGS) + $(GTK_CFLAGS) \ + $(FARSIGHT_CFLAGS)
--- a/pidgin/plugins/musicmessaging/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/plugins/musicmessaging/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -40,5 +40,6 @@ -I$(top_srcdir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GTK_CFLAGS) \ $(DBUS_CFLAGS)
--- a/pidgin/plugins/ticker/Makefile.am Tue May 27 04:11:04 2008 +0000 +++ b/pidgin/plugins/ticker/Makefile.am Tue May 27 06:27:46 2008 +0000 @@ -24,4 +24,5 @@ -I$(top_builddir)/libpurple \ -I$(top_srcdir)/pidgin \ $(DEBUG_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ $(GTK_CFLAGS)
--- a/po/POTFILES.in Tue May 27 04:11:04 2008 +0000 +++ b/po/POTFILES.in Tue May 27 06:27:46 2008 +0000 @@ -8,6 +8,7 @@ finch/gntdebug.c finch/gntft.c finch/gntlog.c +finch/gntmedia.c finch/gntnotify.c finch/gntplugin.c finch/gntpounce.c @@ -86,6 +87,7 @@ libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/chat.c libpurple/protocols/jabber/jabber.c +libpurple/protocols/jabber/jingle.c libpurple/protocols/jabber/libxmpp.c libpurple/protocols/jabber/message.c libpurple/protocols/jabber/parser.c