# HG changeset patch # User Mike Ruprecht # Date 1211905826 0 # Node ID 7d2e5f57dbca805359abfef8803154d07730490d # Parent 903fb6879521a18fcc2bc619b7faff990d753dbb# Parent e17dbd941380f93a8477759aec7388795f4430b6 propagate from branch 'im.pidgin.pidgin.vv' (head 9443004596a4fc55992da8548ef5b438de22c8ef) to branch 'im.pidgin.soc.2008.vv' (head 99d72a9b9b6887865627081565f052f29516d19b) diff -r 903fb6879521 -r 7d2e5f57dbca configure.ac --- a/configure.ac Tue May 27 06:51:21 2008 +0000 +++ b/configure.ac Tue May 27 16:30:26 2008 +0000 @@ -325,6 +325,9 @@ AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) +GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0` +AC_SUBST(GLIB_GENMARSHAL) + AC_ARG_WITH([extraversion], AC_HELP_STRING([--with-extraversion=STRING], [extra version number to be displayed in Help->About and --help (for packagers)]), @@ -728,16 +731,15 @@ dnl ####################################################################### dnl # Check for Farsight dnl ####################################################################### -AC_ARG_ENABLE(farsight, +AC_ARG_ENABLE(vv, [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], [ + PKG_CHECK_MODULES(FARSIGHT, [farsight2-0.10 gstreamer-0.10 gstreamer-plugins-base-0.10 libxml-2.0], [ 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 @@ -759,7 +761,6 @@ AC_SUBST(GSTPROPS_LIBS) AC_SUBST(GSTPROPS_CFLAGS) ], [ - AC_MSG_RESULT(no) enable_gstprops="no" ]) fi diff -r 903fb6879521 -r 7d2e5f57dbca finch/gntmedia.c --- a/finch/gntmedia.c Tue May 27 06:51:21 2008 +0000 +++ b/finch/gntmedia.c Tue May 27 16:30:26 2008 +0000 @@ -42,8 +42,6 @@ /* An incredibly large part of the following is from gtkmedia.c */ #ifdef USE_FARSIGHT -#include - #undef hangup struct _FinchMediaPrivate @@ -390,8 +388,6 @@ NULL)); } -#endif /* USE_FARSIGHT */ - static void gntmedia_message_cb(FinchMedia *gntmedia, const char *msg, PurpleConversation *conv) { @@ -458,3 +454,5 @@ G_CALLBACK(finch_new_media), NULL); } +#endif /* USE_FARSIGHT */ + diff -r 903fb6879521 -r 7d2e5f57dbca finch/gntmedia.h --- a/finch/gntmedia.h Tue May 27 06:51:21 2008 +0000 +++ b/finch/gntmedia.h Tue May 27 16:30:26 2008 +0000 @@ -33,7 +33,6 @@ #ifdef USE_FARSIGHT -#include #include #include "gntbox.h" diff -r 903fb6879521 -r 7d2e5f57dbca finch/gntui.c --- a/finch/gntui.c Tue May 27 06:51:21 2008 +0000 +++ b/finch/gntui.c Tue May 27 16:30:26 2008 +0000 @@ -92,8 +92,10 @@ finch_roomlist_init(); purple_roomlist_set_ui_ops(finch_roomlist_get_ui_ops()); +#ifdef USE_FARSIGHT /* Media */ finch_media_manager_init(); +#endif gnt_register_action(_("Accounts"), finch_accounts_show_all); gnt_register_action(_("Buddy List"), finch_blist_show); @@ -140,7 +142,9 @@ finch_roomlist_uninit(); purple_roomlist_set_ui_ops(NULL); +#ifdef USE_FARSIGHT finch_media_manager_uninit(); +#endif gnt_quit(); #endif diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/Makefile.am --- a/libpurple/Makefile.am Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/Makefile.am Tue May 27 16:30:26 2008 +0000 @@ -1,6 +1,7 @@ EXTRA_DIST = \ dbus-analyze-functions.py \ dbus-analyze-types.py \ + marshallers.list \ purple-notifications-example \ purple-remote \ purple-send \ @@ -51,6 +52,7 @@ idle.c \ imgstore.c \ log.c \ + marshallers.c \ media.c \ mediamanager.c \ mime.c \ @@ -106,6 +108,7 @@ idle.h \ imgstore.h \ log.h \ + marshallers.h \ media.h \ mediamanager.h \ mime.h \ @@ -141,6 +144,15 @@ purple_builtheaders = purple.h version.h +marshallers.h: marshallers.list + @echo "Generating marshallers.h" + $(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h + +marshallers.c: marshallers.list + @echo "Generating marshallers.c" + echo "#include \"marshallers.h\"" > marshallers.c + $(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c + if ENABLE_DBUS CLEANFILES = \ @@ -149,6 +161,8 @@ dbus-client-binding.h \ dbus-types.c \ dbus-types.h \ + marshallers.c \ + marshallers.h \ purple-client-bindings.c \ purple-client-bindings.h \ purple.service @@ -219,6 +233,8 @@ dbus-types.c \ dbus-types.h \ dbus-bindings.c \ + marshallers.c \ + marshallers.h \ purple-client-bindings.c \ purple-client-bindings.h diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/marshallers.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/marshallers.list Tue May 27 16:30:26 2008 +0000 @@ -0,0 +1,1 @@ +VOID:BOXED,BOXED diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/media.c --- a/libpurple/media.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/media.c Tue May 27 16:30:26 2008 +0000 @@ -29,6 +29,7 @@ #include "connection.h" #include "media.h" +#include "marshallers.h" #include "debug.h" @@ -36,11 +37,11 @@ #ifdef USE_GSTPROPS #include -#include +#include struct _PurpleMediaPrivate { - FarsightSession *farsight_session; + FsConference *conference; char *name; PurpleConnection *connection; @@ -49,8 +50,23 @@ GstElement *video_src; GstElement *video_sink; - FarsightStream *audio_stream; - FarsightStream *video_stream; + FsSession *audio_session; + FsSession *video_session; + + GList *participants; /* FsParticipant list */ + GList *audio_streams; /* FsStream list */ + GList *video_streams; /* FsStream list */ + + /* might be able to just combine these two */ + GstElement *audio_pipeline; + GstElement *video_pipeline; + + /* this will need to be stored/handled per stream + * once having multiple streams is supported */ + GList *local_candidates; + + FsCandidate *local_candidate; + FsCandidate *remote_candidate; }; #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate)) @@ -73,21 +89,23 @@ REJECT, GOT_HANGUP, GOT_ACCEPT, + CANDIDATES_PREPARED, + CANDIDATE_PAIR, LAST_SIGNAL }; static guint purple_media_signals[LAST_SIGNAL] = {0}; enum { PROP_0, - PROP_FARSIGHT_SESSION, + PROP_FS_CONFERENCE, PROP_NAME, PROP_CONNECTION, PROP_AUDIO_SRC, PROP_AUDIO_SINK, PROP_VIDEO_SRC, PROP_VIDEO_SINK, - PROP_VIDEO_STREAM, - PROP_AUDIO_STREAM + PROP_VIDEO_SESSION, + PROP_AUDIO_SESSION }; GType @@ -113,7 +131,6 @@ return type; } - static void purple_media_class_init (PurpleMediaClass *klass) { @@ -124,11 +141,11 @@ 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_object_class_install_property(gobject_class, PROP_FS_CONFERENCE, + g_param_spec_object("farsight-conference", + "Farsight conference", + "The FsConference associated with this media.", + FS_TYPE_CONFERENCE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_NAME, @@ -172,18 +189,18 @@ GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - g_object_class_install_property(gobject_class, PROP_VIDEO_STREAM, - g_param_spec_object("video-stream", + g_object_class_install_property(gobject_class, PROP_VIDEO_SESSION, + g_param_spec_object("video-session", "Video stream", "The FarsightStream used for video", - FARSIGHT_TYPE_STREAM, + FS_TYPE_SESSION, G_PARAM_READWRITE)); - g_object_class_install_property(gobject_class, PROP_AUDIO_STREAM, - g_param_spec_object("audio-stream", + g_object_class_install_property(gobject_class, PROP_AUDIO_SESSION, + g_param_spec_object("audio-session", "Audio stream", "The FarsightStream used for audio", - FARSIGHT_TYPE_STREAM, + FS_TYPE_SESSION, G_PARAM_READWRITE)); purple_media_signals[READY] = g_signal_new("ready", G_TYPE_FROM_CLASS(klass), @@ -214,6 +231,14 @@ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + purple_media_signals[CANDIDATE_PAIR] = g_signal_new("candidate-pair", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + purple_smarshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, FS_TYPE_CANDIDATE, FS_TYPE_CANDIDATE); g_type_class_add_private(klass, sizeof(PurpleMediaPrivate)); } @@ -241,11 +266,11 @@ 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); + case PROP_FS_CONFERENCE: + if (media->priv->conference) + g_object_unref(media->priv->conference); + media->priv->conference = g_value_get_object(value); + g_object_ref(media->priv->conference); break; case PROP_NAME: g_free(media->priv->name); @@ -259,12 +284,16 @@ gst_object_unref(media->priv->audio_src); media->priv->audio_src = g_value_get_object(value); gst_object_ref(media->priv->audio_src); + gst_bin_add(GST_BIN(purple_media_get_audio_pipeline(media)), + 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); + gst_bin_add(GST_BIN(purple_media_get_audio_pipeline(media)), + media->priv->audio_sink); break; case PROP_VIDEO_SRC: if (media->priv->video_src) @@ -278,17 +307,17 @@ 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); + case PROP_VIDEO_SESSION: + if (media->priv->video_session) + g_object_unref(media->priv->video_session); + media->priv->video_session = g_value_get_object(value); + gst_object_ref(media->priv->video_session); 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); + case PROP_AUDIO_SESSION: + if (media->priv->audio_session) + g_object_unref(media->priv->audio_session); + media->priv->audio_session = g_value_get_object(value); + gst_object_ref(media->priv->audio_session); break; default: @@ -306,8 +335,8 @@ media = PURPLE_MEDIA(object); switch (prop_id) { - case PROP_FARSIGHT_SESSION: - g_value_set_object(value, media->priv->farsight_session); + case PROP_FS_CONFERENCE: + g_value_set_object(value, media->priv->conference); break; case PROP_NAME: g_value_set_string(value, media->priv->name); @@ -327,11 +356,11 @@ 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); + case PROP_VIDEO_SESSION: + g_value_set_object(value, media->priv->video_session); break; - case PROP_AUDIO_STREAM: - g_value_set_object(value, media->priv->audio_stream); + case PROP_AUDIO_SESSION: + g_value_set_object(value, media->priv->audio_session); break; default: @@ -415,12 +444,12 @@ 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); + if (!media->priv->audio_pipeline) { + media->priv->audio_pipeline = gst_pipeline_new(media->priv->name); + gst_bin_add(GST_BIN(media->priv->audio_pipeline), GST_ELEMENT(media->priv->conference)); + } + + return media->priv->audio_pipeline; } PurpleConnection * @@ -517,7 +546,7 @@ !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", + purple_debug_info("media", "Found source '%s' (%s) - no device\n", longname, GST_PLUGIN_FEATURE (factory)->name); } else { gint n; @@ -542,6 +571,12 @@ gst_element_set_state (element, GST_STATE_NULL); ret = g_list_append(ret, device); + + name = purple_media_get_device_name(GST_ELEMENT(element), device); + purple_debug_info("media", "Found source '%s' (%s) - device '%s' (%s)\n", + longname, GST_PLUGIN_FEATURE (factory)->name, + name, g_value_get_string(device)); + g_free(name); } } } @@ -631,5 +666,255 @@ purple_debug_info("media", "purple_media_audio_init_recv end\n"); } +static void +purple_media_new_local_candidate(FsStream *stream, + FsCandidate *local_candidate, + PurpleMedia *media) +{ + purple_debug_info("media", "got new local candidate: %s\n", local_candidate->candidate_id); + media->priv->local_candidates = g_list_append(media->priv->local_candidates, + fs_candidate_copy(local_candidate)); +} + +static void +purple_media_candidates_prepared(FsStream *stream, PurpleMedia *media) +{ + g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED], 0); +} + +/* callback called when a pair of transport candidates (local and remote) + * has been established */ +static void +purple_media_candidate_pair_established(FsStream *stream, + FsCandidate *native_candidate, + FsCandidate *remote_candidate, + PurpleMedia *media) +{ + media->priv->local_candidate = fs_candidate_copy(native_candidate); + media->priv->remote_candidate = fs_candidate_copy(remote_candidate); + + purple_debug_info("media", "candidate pair established\n"); + g_signal_emit(media, purple_media_signals[CANDIDATE_PAIR], 0, + media->priv->local_candidate, + media->priv->remote_candidate); +} + +static void +purple_media_src_pad_added(FsStream *stream, GstPad *srcpad, + FsCodec *codec, PurpleMedia *media) +{ + GstElement *pipeline = purple_media_get_audio_pipeline(media); + GstPad *sinkpad = gst_element_get_static_pad(purple_media_get_audio_sink(media), "ghostsink"); + purple_debug_info("media", "connecting new src pad: %s\n", + gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK ? "success" : "failure"); + gst_element_set_state(pipeline, GST_STATE_PLAYING); +} + +static gboolean +purple_media_add_stream_internal(PurpleMedia *media, FsSession **session, GList **streams, + GstElement *src, const gchar *who, FsMediaType type, + FsStreamDirection type_direction, const gchar *transmitter) +{ + char *cname = NULL; + FsParticipant *participant = NULL; + GList *l = NULL; + FsStream *stream = NULL; + FsParticipant *p = NULL; + FsStreamDirection *direction = NULL; + FsSession *s = NULL; + + if (!*session) { + GError *err = NULL; + *session = fs_conference_new_session(media->priv->conference, type, &err); + + if (err != NULL) { + purple_debug_error("media", "Error creating session: %s\n", err->message); + g_error_free(err); + purple_conv_present_error(who, + purple_connection_get_account(purple_media_get_connection(media)), + _("Error creating session.")); + return FALSE; + } + + if (src) { + GstPad *sinkpad; + GstPad *srcpad; + g_object_get(*session, "sink-pad", &sinkpad, NULL); + srcpad = gst_element_get_static_pad(src, "ghostsrc"); + purple_debug_info("media", "connecting pad: %s\n", + gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK + ? "success" : "failure"); + } + } + + for (l = media->priv->participants; l != NULL; l = g_list_next(l)) { + g_object_get(l->data, "cname", cname, NULL); + if (!strcmp(cname, who)) { + g_free(cname); + participant = l->data; + break; + } + g_free(cname); + } + + if (!participant) { + participant = fs_conference_new_participant(media->priv->conference, (gchar*)who, NULL); + media->priv->participants = g_list_prepend(media->priv->participants, participant); + } + + for (l = *streams; l != NULL; l = g_list_next(l)) { + g_object_get(l->data, "participant", &p, "direction", &direction, "session", &s, NULL); + + if (participant == p && *session == s) { + stream = l->data; + break; + } + } + + if (!stream) { + stream = fs_session_new_stream(*session, participant, + type_direction, transmitter, 0, NULL, NULL); + *streams = g_list_prepend(*streams, stream); + /* callback for new local candidate (new local candidate retreived) */ + g_signal_connect(G_OBJECT(stream), + "new-local-candidate", G_CALLBACK(purple_media_new_local_candidate), media); + /* callback for source pad added (new stream source ready) */ + g_signal_connect(G_OBJECT(stream), + "src-pad-added", G_CALLBACK(purple_media_src_pad_added), media); + /* callback for local candidates prepared (local candidates ready to send) */ + g_signal_connect(G_OBJECT(stream), + "local-candidates-prepared", + G_CALLBACK(purple_media_candidates_prepared), media); + /* callback for new active candidate pair (established connection) */ + g_signal_connect(G_OBJECT(stream), + "new-active-candidate-pair", + G_CALLBACK(purple_media_candidate_pair_established), media); + } else if (*direction != type_direction) { + /* change direction */ + g_object_set(stream, "direction", type_direction, NULL); + } + + return TRUE; +} + +gboolean +purple_media_add_stream(PurpleMedia *media, const gchar *who, + PurpleMediaStreamType type, + const gchar *transmitter) +{ + FsStreamDirection type_direction; + + if (type & PURPLE_MEDIA_AUDIO) { + if (type & PURPLE_MEDIA_SEND_AUDIO && type & PURPLE_MEDIA_RECV_AUDIO) + type_direction = FS_DIRECTION_BOTH; + else if (type & PURPLE_MEDIA_SEND_AUDIO) + type_direction = FS_DIRECTION_SEND; + else if (type & PURPLE_MEDIA_RECV_AUDIO) + type_direction = FS_DIRECTION_RECV; + else + type_direction = FS_DIRECTION_NONE; + + if (!purple_media_add_stream_internal(media, &media->priv->audio_session, + &media->priv->audio_streams, + media->priv->audio_src, who, + FS_MEDIA_TYPE_AUDIO, type_direction, + transmitter)) { + return FALSE; + } + } + if (type & PURPLE_MEDIA_VIDEO) { + if (type & PURPLE_MEDIA_SEND_VIDEO && type & PURPLE_MEDIA_RECV_VIDEO) + type_direction = FS_DIRECTION_BOTH; + else if (type & PURPLE_MEDIA_SEND_VIDEO) + type_direction = FS_DIRECTION_SEND; + else if (type & PURPLE_MEDIA_RECV_VIDEO) + type_direction = FS_DIRECTION_RECV; + else + type_direction = FS_DIRECTION_NONE; + + if (!purple_media_add_stream_internal(media, &media->priv->video_session, + &media->priv->video_streams, + media->priv->video_src, who, + FS_MEDIA_TYPE_VIDEO, type_direction, + transmitter)) { + return FALSE; + } + } + return TRUE; +} + +void +purple_media_remove_stream(PurpleMedia *media, const gchar *who, PurpleMediaStreamType type) +{ + +} + +static FsStream * +purple_media_get_audio_stream(PurpleMedia *media, const gchar *name) +{ + GList *streams = media->priv->audio_streams; + for (; streams; streams = streams->next) { + FsParticipant *participant; + gchar *cname; + g_object_get(streams->data, "participant", &participant, NULL); + g_object_get(participant, "cname", &cname, NULL); + + if (!strcmp(cname, name)) { + return streams->data; + } + } + + return NULL; +} + +GList * +purple_media_get_local_audio_codecs(PurpleMedia *media) +{ + GList *codecs; + g_object_get(G_OBJECT(media->priv->audio_session), "local-codecs", &codecs, NULL); + return codecs; +} + +GList * +purple_media_get_local_audio_candidates(PurpleMedia *media) +{ + return media->priv->local_candidates; +} + +GList * +purple_media_get_negotiated_audio_codecs(PurpleMedia *media) +{ + GList *codec_intersection; + g_object_get(media->priv->audio_session, "negotiated-codecs", &codec_intersection, NULL); + return codec_intersection; +} + +void +purple_media_add_remote_audio_candidates(PurpleMedia *media, const gchar *name, GList *remote_candidates) +{ + FsStream *stream = purple_media_get_audio_stream(media, name); + GList *candidates = remote_candidates; + for (; candidates; candidates = candidates->next) + fs_stream_add_remote_candidate(stream, candidates->data, NULL); +} + +FsCandidate * +purple_media_get_local_candidate(PurpleMedia *media) +{ + return media->priv->local_candidate; +} + +FsCandidate * +purple_media_get_remote_candidate(PurpleMedia *media) +{ + return media->priv->remote_candidate; +} + +void +purple_media_set_remote_audio_codecs(PurpleMedia *media, const gchar *name, GList *codecs) +{ + fs_stream_set_remote_codecs(purple_media_get_audio_stream(media, name), codecs, NULL); +} + #endif /* USE_GSTPROPS */ #endif /* USE_FARSIGHT */ diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/media.h --- a/libpurple/media.h Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/media.h Tue May 27 16:30:26 2008 +0000 @@ -29,7 +29,9 @@ #ifdef USE_FARSIGHT #ifdef USE_GSTPROPS -#include +#include +#include +#include #include #include @@ -53,6 +55,8 @@ PURPLE_MEDIA_SEND_AUDIO = 1 << 1, PURPLE_MEDIA_RECV_VIDEO = 1 << 2, PURPLE_MEDIA_SEND_VIDEO = 1 << 3, + PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO, + PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO } PurpleMediaStreamType; struct _PurpleMediaClass @@ -109,6 +113,20 @@ void purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel); +gboolean purple_media_add_stream(PurpleMedia *media, const gchar *who, + PurpleMediaStreamType type, const gchar *transmitter); +void purple_media_remove_stream(PurpleMedia *media, const gchar *who, PurpleMediaStreamType type); + +GList *purple_media_get_local_audio_candidates(PurpleMedia *media); +GList *purple_media_get_negotiated_audio_codecs(PurpleMedia *media); + +GList *purple_media_get_local_audio_codecs(PurpleMedia *media); +void purple_media_add_remote_audio_candidates(PurpleMedia *media, const gchar *name, + GList *remote_candidates); +FsCandidate *purple_media_get_local_candidate(PurpleMedia *media); +FsCandidate *purple_media_get_remote_candidate(PurpleMedia *media); +void purple_media_set_remote_audio_codecs(PurpleMedia *media, const gchar *name, GList *codecs); + G_END_DECLS #endif /* USE_GSTPROPS */ diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/mediamanager.c --- a/libpurple/mediamanager.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/mediamanager.c Tue May 27 16:30:26 2008 +0000 @@ -31,7 +31,7 @@ #ifdef USE_FARSIGHT -#include +#include struct _PurpleMediaManagerPrivate { @@ -127,18 +127,28 @@ return manager; } -PurpleMedia* -purple_media_manager_create_media(PurpleMediaManager *manager, +PurpleMedia * +purple_media_manager_create_media(PurpleMediaManager *manager, PurpleConnection *gc, - const char *screenname, - FarsightStream *audio_stream, - FarsightStream *video_stream) + const char *conference_type, + const char *remote_user) { - PurpleMedia *media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), - "screenname", screenname, - "connection", gc, - "audio-stream", audio_stream, - "video-stream", video_stream, NULL)); + PurpleMedia *media; + FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL)); + GstStateChangeReturn ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_READY); + + if (ret == GST_STATE_CHANGE_FAILURE) { + purple_conv_present_error(remote_user, + purple_connection_get_account(gc), + _("Error creating conference.")); + return NULL; + } + + media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), + "screenname", remote_user, + "connection", gc, + "farsight-conference", conference, + 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; diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/mediamanager.h --- a/libpurple/mediamanager.h Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/mediamanager.h Tue May 27 16:30:26 2008 +0000 @@ -28,7 +28,7 @@ #ifdef USE_FARSIGHT -#include +#include #include #include @@ -61,12 +61,10 @@ 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); + PurpleConnection *gc, + const char *conference_type, + const char *remote_user); G_END_DECLS diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/protocols/jabber/google.c --- a/libpurple/protocols/jabber/google.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/protocols/jabber/google.c Tue May 27 16:30:26 2008 +0000 @@ -18,8 +18,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include - #include "internal.h" #include "debug.h" #include "mediamanager.h" @@ -32,6 +30,9 @@ #include "presence.h" #include "iq.h" +#ifdef USE_FARSIGHT +#include + typedef struct { char *id; char *initiator; @@ -49,7 +50,6 @@ GoogleSessionId id; GoogleSessionState state; PurpleMedia *media; - FarsightStream *stream; JabberStream *js; char *remote_jid; } GoogleSession; @@ -84,7 +84,6 @@ g_free(session->id.initiator); g_free(session->remote_jid); g_object_unref(session->media); - g_object_unref(session->stream); g_free(session); } @@ -103,7 +102,7 @@ google_session_send_accept(GoogleSession *session) { xmlnode *sess, *desc, *payload; - GList *codecs = farsight_stream_get_codec_intersection(session->stream); + GList *codecs = purple_media_get_negotiated_audio_codecs(session->media); JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); xmlnode_set_attrib(iq->node, "to", session->remote_jid); @@ -113,7 +112,7 @@ xmlnode_set_namespace(desc, "http://www.google.com/session/phone"); for (;codecs; codecs = codecs->next) { - FarsightCodec *codec = (FarsightCodec*)codecs->data; + FsCodec *codec = (FsCodec*)codecs->data; char id[8], clockrate[10]; payload = xmlnode_new_child(desc, "payload-type"); g_snprintf(id, sizeof(id), "%d", codec->id); @@ -123,15 +122,15 @@ xmlnode_set_attrib(payload, "clockrate", clockrate); } + fs_codec_list_destroy(codecs); jabber_iq_send(iq); - farsight_stream_start(session->stream); + gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING); } 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); @@ -139,7 +138,6 @@ xmlnode_insert_child(iq->node, sess); jabber_iq_send(iq); - farsight_stream_stop(session->stream); google_session_destroy(session); } @@ -147,7 +145,6 @@ 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); @@ -155,17 +152,16 @@ 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) +google_session_candidates_prepared (PurpleMedia *media, GoogleSession *session) { JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); - GList *candidates = farsight_stream_get_native_candidate_list(stream); - FarsightTransportInfo *transport; + GList *candidates = purple_media_get_local_audio_candidates(session->media); + FsCandidate *transport; xmlnode *sess; xmlnode *candidate; sess = google_session_create_xmlnode(session, "candidates"); @@ -173,9 +169,9 @@ xmlnode_set_attrib(iq->node, "to", session->remote_jid); for (;candidates;candidates = candidates->next) { - transport = (FarsightTransportInfo*)(candidates->data); char port[8]; char pref[8]; + transport = (FsCandidate*)(candidates->data); if (!strcmp(transport->ip, "127.0.0.1")) continue; @@ -183,7 +179,7 @@ candidate = xmlnode_new("candidate"); g_snprintf(port, sizeof(port), "%d", transport->port); - g_snprintf(pref, sizeof(pref), "%f", transport->preference); + g_snprintf(pref, sizeof(pref), "%d", transport->priority); xmlnode_set_attrib(candidate, "address", transport->ip); xmlnode_set_attrib(candidate, "port", port); @@ -191,10 +187,10 @@ 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, "protocol", transport->proto == FS_NETWORK_PROTOCOL_UDP ? "udp" : "tcp"); + xmlnode_set_attrib(candidate, "type", transport->type == FS_CANDIDATE_TYPE_HOST ? "local" : + transport->type == FS_CANDIDATE_TYPE_PRFLX ? "stun" : + transport->type == FS_CANDIDATE_TYPE_RELAY ? "relay" : NULL); xmlnode_set_attrib(candidate, "generation", "0"); xmlnode_set_attrib(candidate, "network", "0"); xmlnode_insert_child(sess, candidate); @@ -206,30 +202,24 @@ 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; + FsCodec *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; + return; } - 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); + session->media = purple_media_manager_create_media(purple_media_manager_get(), js->gc, + "fsrtpconference", session->remote_jid); - g_object_set(G_OBJECT(session->stream), "transmitter", "libjingle", NULL); - + /* "rawudp" will need to be changed to "nice" when libnice is finished */ + purple_media_add_stream(session->media, session->remote_jid, + PURPLE_MEDIA_AUDIO, "rawudp"); + desc_element = xmlnode_get_child(sess, "description"); for (codec_element = xmlnode_get_child(desc_element, "payload-type"); @@ -239,31 +229,25 @@ 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); + codec = fs_codec_new(atoi(id), encoding_name, FS_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); + purple_media_set_remote_audio_codecs(session->media, session->remote_jid, codecs); - 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); + g_signal_connect_swapped(G_OBJECT(session->media), "accepted", + G_CALLBACK(google_session_send_accept), session); + g_signal_connect_swapped(G_OBJECT(session->media), "reject", + G_CALLBACK(google_session_send_reject), session); + g_signal_connect_swapped(G_OBJECT(session->media), "hangup", + G_CALLBACK(google_session_send_terminate), session); + g_signal_connect(G_OBJECT(session->media), "candidates-prepared", + G_CALLBACK(google_session_candidates_prepared), session); + purple_media_ready(session->media); - - GstElement *e = purple_media_get_audio_src(media); - farsight_stream_set_source(session->stream, e); + fs_codec_list_destroy(codecs); - 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); @@ -280,24 +264,26 @@ char n[4]; for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) { - FarsightTransportInfo *info = g_new0(FarsightTransportInfo, 1); + FsCandidate *info; 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"); + info = fs_candidate_new(n, FS_COMPONENT_RTP, !strcmp(xmlnode_get_attrib(cand, "type"), "local") ? + FS_CANDIDATE_TYPE_HOST : + !strcmp(xmlnode_get_attrib(cand, "type"), "stun") ? + FS_CANDIDATE_TYPE_PRFLX : + !strcmp(xmlnode_get_attrib(cand, "type"), "relay") ? + FS_CANDIDATE_TYPE_RELAY : FS_CANDIDATE_TYPE_HOST, + !strcmp(xmlnode_get_attrib(cand, "protocol"),"udp") ? + FS_NETWORK_PROTOCOL_UDP : FS_NETWORK_PROTOCOL_TCP, + xmlnode_get_attrib(cand, "address"), atoi(xmlnode_get_attrib(cand, "port"))); + + info->username = g_strdup(xmlnode_get_attrib(cand, "username")); + info->password = g_strdup(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); + purple_media_add_remote_audio_candidates(session->media, session->remote_jid, list); + fs_candidate_list_destroy(list); result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); @@ -308,7 +294,6 @@ 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); @@ -317,7 +302,6 @@ 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); @@ -340,13 +324,14 @@ google_session_handle_candidates(js, session, packet, sess); } } +#endif /* USE_FARSIGHT */ void jabber_google_session_parse(JabberStream *js, xmlnode *packet) { +#ifdef USE_FARSIGHT GoogleSession *session; GoogleSessionId id; - JabberIq *result; xmlnode *session_node; xmlnode *desc_node; @@ -358,11 +343,11 @@ if (!session_node) return; - id.id = xmlnode_get_attrib(session_node, "id"); + id.id = (gchar*)xmlnode_get_attrib(session_node, "id"); if (!id.id) return; - id.initiator = xmlnode_get_attrib(session_node, "initiator"); + id.initiator = (gchar*)xmlnode_get_attrib(session_node, "initiator"); if (!id.initiator) return; @@ -390,6 +375,9 @@ g_hash_table_insert(sessions, &(session->id), session); google_session_parse_iq(js, session, packet); +#else + /* TODO: send proper error response */ +#endif /* USE_FARSIGHT */ } static void diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Tue May 27 16:30:26 2008 +0000 @@ -58,6 +58,10 @@ #include "adhoccommands.h" #include "jingle.h" +#ifdef USE_FARSIGHT +#include +#endif + #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) static PurplePlugin *my_protocol = NULL; @@ -630,6 +634,10 @@ js->keepalive_timeout = -1; js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain); +#ifdef USE_FARSIGHT + js->sessions = NULL; +#endif + if(!js->user) { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, @@ -1240,6 +1248,19 @@ { JabberStream *js = gc->proto_data; +#ifdef USE_FARSIGHT + /* Close all of the open media sessions on this stream */ + GList *values = g_hash_table_get_values(js->sessions); + GList *iter = values; + + for (; iter; iter = iter->next) { + JingleSession *session = (JingleSession *)iter->data; + purple_media_hangup(session->media); + } + + g_list_free(values); +#endif + /* Don't perform any actions on the ssl connection * if we were forcibly disconnected because it will crash * on some SSL backends. @@ -1863,10 +1884,20 @@ JabberID *jid; JabberBuddy *jb; JabberBuddyResource *jbr; - +#ifdef USE_FARSIGHT + JingleSession *session; +#endif if(!(jid = jabber_id_new(who))) return; +#ifdef USE_FARSIGHT + session = jabber_jingle_session_find_by_jid(js, who); + + if (session) { + purple_media_hangup(session->media); + } + +#endif if((jb = jabber_buddy_find(js, who, TRUE)) && (jbr = jabber_buddy_find_resource(jb, jid->resource))) { if(jbr->thread_id) { @@ -2358,11 +2389,9 @@ 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)); - + gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING); + + session->session_started = TRUE; } static void @@ -2389,7 +2418,6 @@ 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); } @@ -2404,117 +2432,147 @@ 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) +jabber_session_candidates_prepared(PurpleMedia *media, 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); + if (!jabber_jingle_session_is_initiator(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); + purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n", + g_list_length(purple_media_get_local_audio_candidates(session->media))); + 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) +jabber_session_candidate_pair_established(PurpleMedia *media, + FsCandidate *native_candidate, + FsCandidate *remote_candidate, + JingleSession *session) { - purple_debug_info("jabber", "jabber_candidate_pair_established called"); + purple_debug_info("jabber", "jabber_candidate_pair_established called\n"); /* 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); + JabberIq *result; + xmlnode *jingle; + + purple_debug_info("jabber", "we are the initiator, let's send content-modify\n"); + + result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET); + /* shall change this to a "content-replace" */ - xmlnode *jingle = + jingle = jabber_jingle_session_create_content_replace(session, - native_candidate_id, - remote_candidate_id); + native_candidate, + remote_candidate); 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, +static gboolean +jabber_initiate_media_internal(JingleSession *session, const char *initiator, const char *remote_jid) +{ + PurpleMedia *media = NULL; + + media = purple_media_manager_create_media(purple_media_manager_get(), + session->js->gc, "fsrtpconference", remote_jid); + + if (!media) { + purple_debug_error("jabber", "Couldn't create fsrtpconference\n"); + return FALSE; + } + + /* this will need to be changed to "nice" once the libnice transmitter is finished */ + if (!purple_media_add_stream(media, remote_jid, PURPLE_MEDIA_AUDIO, "rawudp")) { + purple_debug_error("jabber", "Couldn't create audio stream\n"); + purple_media_reject(media); + return FALSE; + } + + jabber_jingle_session_set_remote_jid(session, remote_jid); + jabber_jingle_session_set_initiator(session, initiator); + jabber_jingle_session_set_media(session, media); + + /* connect callbacks */ + 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); + g_signal_connect(G_OBJECT(media), "candidates-prepared", + G_CALLBACK(jabber_session_candidates_prepared), session); + g_signal_connect(G_OBJECT(media), "candidate-pair", + G_CALLBACK(jabber_session_candidate_pair_established), session); + + purple_media_ready(media); + + return TRUE; +} + +static void +jabber_session_initiate_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +{ + const char *from = xmlnode_get_attrib(packet, "from"); + JingleSession *session = jabber_jingle_session_find_by_jid(js, from); + PurpleMedia *media = session->media; + JabberIq *result; + xmlnode *jingle; + + if (!strcmp(xmlnode_get_attrib(packet, "type"), "error")) { + purple_media_got_hangup(media); + return; + } + + /* catch errors */ + if (xmlnode_get_child(packet, "error")) { + purple_media_got_hangup(media); + return; + } + + /* create transport-info package */ + result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET); + jingle = jabber_jingle_session_create_transport_info(session); + purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n", + g_list_length(purple_media_get_local_audio_candidates(session->media))); + xmlnode_set_attrib(result->node, "to", + jabber_jingle_session_get_remote_jid(session)); + + xmlnode_insert_child(result->node, jingle); + jabber_iq_send(result); +} + +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... */ + xmlnode *jingle, *content, *description, *transport; 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"); + char *jid = NULL, *me = NULL; + /* construct JID to send to */ jb = jabber_buddy_find(js, who, FALSE); if (!jb) { @@ -2525,42 +2583,29 @@ if (!jbr) { purple_debug_error("jabber", "Could not find buddy's resource\n"); } - - g_snprintf(jid, 255, "%s/%s", who, jbr->name); + + if ((strchr(who, '/') == NULL) && jbr && (jbr->name != NULL)) { + jid = g_strdup_printf("%s/%s", who, jbr->name); + } else { + jid = g_strdup(who); + } 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); - + me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource); + + if (!jabber_initiate_media_internal(session, me, jid)) { + g_free(jid); + g_free(me); + jabber_jingle_session_destroy(session); + return NULL; + } + + g_free(jid); + g_free(me); + + codecs = purple_media_get_local_audio_codecs(session->media); + /* create request */ xmlnode_set_attrib(request->node, "to", @@ -2569,40 +2614,27 @@ 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, "initiator", jabber_jingle_session_get_initiator(session)); 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); - } - + + description = jabber_jingle_session_create_description(session); + xmlnode_insert_child(content, description); + transport = xmlnode_new_child(content, "transport"); - xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp"); - + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp"); + + jabber_iq_set_callback(request, jabber_session_initiate_result_cb, NULL); + /* send request to other part */ jabber_iq_send(request); - return media; + fs_codec_list_destroy(codecs); + + return session->media; } gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, @@ -2620,12 +2652,11 @@ 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); + JingleSession *session = jabber_jingle_session_find_by_id(js, sid); GList *remote_codecs = NULL; GList *remote_transports = NULL; - GList *codec_intersection = NULL; - FarsightCodec *top = NULL; + GList *codec_intersection; + FsCodec *top = NULL; xmlnode *description = NULL; xmlnode *transport = NULL; @@ -2635,7 +2666,7 @@ jabber_jingle_session_get_remote_jid(session)); jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); - description = xmlnode_get_child(jingle, "description"); + description = xmlnode_get_child(content, "description"); transport = xmlnode_get_child(content, "transport"); /* fetch codecs from remote party */ @@ -2651,27 +2682,29 @@ 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_media_set_remote_audio_codecs(session->media, + jabber_jingle_session_get_remote_jid(session), + remote_codecs); + + codec_intersection = purple_media_get_negotiated_audio_codecs(session->media); 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 = (FsCodec *) codec_intersection->data; + purple_debug_info("jabber", "Found a suitable 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); + purple_media_add_remote_audio_candidates(session->media, + jabber_jingle_session_get_remote_jid(session), + remote_transports); + fs_candidate_list_destroy(remote_transports); } if (g_list_length(codec_intersection) == 0 && g_list_length(remote_transports)) { @@ -2686,10 +2719,12 @@ 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)); + gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING); } jabber_iq_send(result); + + session->session_started = TRUE; } void @@ -2698,7 +2733,12 @@ 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); + JingleSession *session = jabber_jingle_session_find_by_id(js, sid); + + if(!session) { + purple_debug_error("jabber", "jabber_handle_session_terminate couldn't find session\n"); + return; + } xmlnode_set_attrib(result->node, "to", jabber_jingle_session_get_remote_jid(session)); @@ -2708,11 +2748,10 @@ /* maybe we should look at the reasoncode to determine if it was a hangup or a reject, and call different callbacks to purple_media */ - + gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_NULL); 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); } @@ -2725,7 +2764,10 @@ 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); + JingleSession *session = jabber_jingle_session_find_by_id(js, sid); + + if(!session) + purple_debug_error("jabber", "jabber_handle_session_candidates couldn't find session\n"); /* send acknowledement */ xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); @@ -2734,10 +2776,10 @@ /* 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); + purple_media_add_remote_audio_candidates(session->media, + xmlnode_get_attrib(packet, "from"), + remote_candidates); + fs_candidate_list_destroy(remote_candidates); } } @@ -2745,25 +2787,28 @@ 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); + JingleSession *session = jabber_jingle_session_find_by_id(js, sid); + + if (!jabber_jingle_session_is_initiator(session) && session->session_started) { + JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); + JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET); + xmlnode *content_accept = NULL; + + /* 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 @@ -2773,18 +2818,10 @@ xmlnode *jingle = xmlnode_get_child(packet, "jingle"); xmlnode *content = NULL; xmlnode *description = NULL; - char *sid = NULL; - char *initiator = NULL; + const char *sid = NULL; + const 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"); @@ -2792,14 +2829,14 @@ } sid = xmlnode_get_attrib(jingle, "sid"); - //initiator = xmlnode_get_attrib(jingle, "initiator"); - initiator = xmlnode_get_attrib(packet, "from"); + initiator = xmlnode_get_attrib(jingle, "initiator"); + + if (jabber_jingle_session_find_by_id(js, sid)) { + /* This should only happen if you start a session with yourself */ + purple_debug_error("jabber", "Jingle session with id={%s} already exists\n", sid); + return; + } 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"); @@ -2812,92 +2849,26 @@ description = xmlnode_get_child(content, "description"); if (!description) { - purple_debug_error("jabber", "content tag must contain description tag"); + purple_debug_error("jabber", "content tag must contain description tag\n"); + /* we should create an error iq here */ + return; + } + + if (!jabber_initiate_media_internal(session, initiator, initiator)) { + purple_debug_error("jabber", "Couldn't start media session with %s\n", initiator); + jabber_jingle_session_destroy(session); /* 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); + purple_media_set_remote_audio_codecs(session->media, initiator, codecs); 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 diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.h Tue May 27 16:30:26 2008 +0000 @@ -203,6 +203,11 @@ /* A purple timeout tag for the keepalive */ int keepalive_timeout; + +#ifdef USE_FARSIGHT + /* keep a hash table of JingleSessions */ + GHashTable *sessions; +#endif }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace); diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/protocols/jabber/jingle.c --- a/libpurple/protocols/jabber/jingle.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/protocols/jabber/jingle.c Tue May 27 16:30:26 2008 +0000 @@ -28,11 +28,7 @@ #ifdef USE_FARSIGHT -#include -#include - -/* keep a hash table of JingleSessions */ -static GHashTable *sessions = NULL; +#include static gboolean jabber_jingle_session_equal(gconstpointer a, gconstpointer b) @@ -59,17 +55,16 @@ } /* insert it into the hash table */ - if (!sessions) { + if (!js->sessions) { purple_debug_info("jingle", "Creating hash table for sessions\n"); - sessions = g_hash_table_new(g_str_hash, g_str_equal); + js->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; - + g_hash_table_insert(js->sessions, sess->id, sess); + + sess->session_started = FALSE; + return sess; } @@ -104,22 +99,37 @@ void jabber_jingle_session_destroy(JingleSession *sess) { - g_hash_table_remove(sessions, sess->id); + g_hash_table_remove(sess->js->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) +jabber_jingle_session_find_by_id(JabberStream *js, 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: %p\n", js->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); + g_hash_table_size(js->sessions)); + purple_debug_info("jingle", "lookup: %p\n", g_hash_table_lookup(js->sessions, id)); + return (JingleSession *) g_hash_table_lookup(js->sessions, id); +} + +JingleSession *jabber_jingle_session_find_by_jid(JabberStream *js, const char *jid) +{ + GList *values = g_hash_table_get_values(js->sessions); + GList *iter = values; + + for (; iter; iter = iter->next) { + JingleSession *session = (JingleSession *)iter->data; + if (session->js == js && !strcmp(jid, session->remote_jid)) { + g_list_free(values); + return session; + } + } + + g_list_free(values); + return NULL; } GList * @@ -128,7 +138,7 @@ GList *codecs = NULL; xmlnode *codec_element = NULL; const char *encoding_name,*id, *clock_rate; - FarsightCodec *codec; + FsCodec *codec; for (codec_element = xmlnode_get_child(description, "payload-type") ; codec_element ; @@ -137,10 +147,9 @@ 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); + codec = fs_codec_new(atoi(id), encoding_name, + FS_MEDIA_TYPE_AUDIO, + clock_rate ? atoi(clock_rate) : 0); codecs = g_list_append(codecs, codec); } return codecs; @@ -151,48 +160,31 @@ { GList *candidates = NULL; xmlnode *candidate = NULL; - FarsightTransportInfo *ti; + FsCandidate *c; 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"), + c = fs_candidate_new(xmlnode_get_attrib(candidate, "component"), + atoi(xmlnode_get_attrib(candidate, "component")), + strcmp(type, "host") == 0 ? + FS_CANDIDATE_TYPE_HOST : + strcmp(type, "prflx") == 0 ? + FS_CANDIDATE_TYPE_PRFLX : + FS_CANDIDATE_TYPE_RELAY, + 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); + FS_NETWORK_PROTOCOL_UDP : + FS_NETWORK_PROTOCOL_TCP, + xmlnode_get_attrib(candidate, "ip"), + atoi(xmlnode_get_attrib(candidate, "port"))); + candidates = g_list_append(candidates, c); } 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) { @@ -237,36 +229,6 @@ 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) @@ -301,32 +263,105 @@ return jingle; } -static xmlnode * +xmlnode * jabber_jingle_session_create_description(const JingleSession *sess) { - GList *codecs = - farsight_stream_get_local_codecs(jabber_jingle_session_get_stream(sess)); - + GList *codecs = purple_media_get_local_audio_codecs(sess->media); 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]; + FsCodec *codec = (FsCodec*)codecs->data; + char id[8], clockrate[10], channels[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); + g_snprintf(channels, sizeof(channels), "%d", codec->channels); xmlnode_set_attrib(payload, "name", codec->encoding_name); xmlnode_set_attrib(payload, "id", id); xmlnode_set_attrib(payload, "clockrate", clockrate); + xmlnode_set_attrib(payload, "channels", channels); } + fs_codec_list_destroy(codecs); return description; } +static guint +jabber_jingle_get_priority(guint type, guint network) +{ + switch (type) { + case FS_CANDIDATE_TYPE_HOST: + return network == 0 ? 4096 : network == 1 ? 2048 : 1024; + break; + case FS_CANDIDATE_TYPE_PRFLX: + return 126; + break; + case FS_CANDIDATE_TYPE_RELAY: + return 100; + break; + default: + return 0; /* unknown type, should not happen */ + } +} + +static xmlnode * +jabber_jingle_session_create_candidate_info(FsCandidate *c, FsCandidate *remote) +{ + char port[8]; + char prio[8]; + char component[8]; + xmlnode *candidate = NULL; + + candidate = xmlnode_new("candidate"); + + g_snprintf(port, sizeof(port), "%d", c->port); + g_snprintf(prio, sizeof(prio), "%d", + jabber_jingle_get_priority(c->type, 0)); + g_snprintf(component, sizeof(component), "%d", c->component_id); + + 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", c->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", + c->proto == FS_NETWORK_PROTOCOL_UDP ? + "udp" : "tcp"); + if (c->username) + xmlnode_set_attrib(candidate, "ufrag", c->username); + if (c->password) + xmlnode_set_attrib(candidate, "pwd", c->password); + + xmlnode_set_attrib(candidate, "type", + c->type == FS_CANDIDATE_TYPE_HOST ? + "host" : + c->type == FS_CANDIDATE_TYPE_PRFLX ? + "prflx" : + c->type == FS_CANDIDATE_TYPE_RELAY ? + "relay" : NULL); + + /* relay */ + if (c->type == FS_CANDIDATE_TYPE_RELAY) { + /* set rel-addr and rel-port? How? */ + } + + if (remote) { + char remote_port[8]; + g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port); + xmlnode_set_attrib(candidate, "rem-addr", remote->ip); + xmlnode_set_attrib(candidate, "rem-port", remote_port); + } + + return candidate; +} + /* split into two separate methods, one to generate session-accept (includes codecs) and one to generate transport-info (includes transports candidates) */ @@ -337,6 +372,8 @@ jabber_jingle_session_create_jingle_element(sess, "session-accept"); xmlnode *content = NULL; xmlnode *description = NULL; + xmlnode *transport = NULL; + xmlnode *candidate = NULL; content = xmlnode_new_child(jingle, "content"); @@ -345,199 +382,82 @@ xmlnode_set_attrib(content, "profile", "RTP/AVP"); description = jabber_jingle_session_create_description(sess); - xmlnode_insert_child(jingle, description); - + xmlnode_insert_child(content, description); + + transport = xmlnode_new_child(content, "transport"); + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp"); + candidate = jabber_jingle_session_create_candidate_info( + purple_media_get_local_candidate(sess->media), + purple_media_get_remote_candidate(sess->media)); + xmlnode_insert_child(transport, candidate); + 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) +jabber_jingle_session_create_transport_info(const JingleSession *sess) { 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); - + GList *candidates = purple_media_get_local_audio_candidates(sess->media); + 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"); + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp"); /* 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")) { + FsCandidate *c = (FsCandidate *) candidates->data; + + if (!strcmp(c->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); - + + xmlnode_insert_child(transport, + jabber_jingle_session_create_candidate_info(c, NULL)); } - farsight_transport_list_destroy(candidates); + fs_candidate_list_destroy(candidates); return jingle; } xmlnode * jabber_jingle_session_create_content_replace(const JingleSession *sess, - gchar *native_candidate_id, - gchar *remote_candidate_id) + FsCandidate *native_candidate, + FsCandidate *remote_candidate) { 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; - */ - + ", remote candidate %s\n", native_candidate->candidate_id, + remote_candidate->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"); - 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_insert_child(content, jabber_jingle_session_create_description(sess)); - 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 */ - + transport = xmlnode_new_child(content, "transport"); + xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp"); + xmlnode_insert_child(transport, + jabber_jingle_session_create_candidate_info(native_candidate, + remote_candidate)); + purple_debug_info("jingle", "End create content modify\n"); return jingle; @@ -556,24 +476,9 @@ xmlnode_set_attrib(content, "name", "audio-content"); xmlnode_set_attrib(content, "profile", "RTP/AVP"); - xmlnode_insert_child(jingle, description); + xmlnode_insert_child(content, 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 */ diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/protocols/jabber/jingle.h --- a/libpurple/protocols/jabber/jingle.h Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/protocols/jabber/jingle.h Tue May 27 16:30:26 2008 +0000 @@ -26,20 +26,16 @@ #ifdef USE_FARSIGHT -#include - 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; + gboolean session_started; } JingleSession; JingleSession *jabber_jingle_session_create(JabberStream *js); @@ -51,10 +47,8 @@ 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); +JingleSession *jabber_jingle_session_find_by_id(JabberStream *js, const char *id); +JingleSession *jabber_jingle_session_find_by_jid(JabberStream *js, const char *jid); PurpleMedia *jabber_jingle_session_get_media(const JingleSession *sess); void jabber_jingle_session_set_media(JingleSession *sess, PurpleMedia *media); @@ -70,20 +64,16 @@ 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_transport_info(const JingleSession *sess); xmlnode *jabber_jingle_session_create_content_replace(const JingleSession *sess, - gchar *native_candidate_id, - gchar *remote_candidate_id); + FsCandidate *native_candidate, + FsCandidate *remote_candidate); xmlnode *jabber_jingle_session_create_content_accept(const JingleSession *sess); +xmlnode *jabber_jingle_session_create_description(const JingleSession *sess); /** * Gets a list of Farsight codecs from a Jingle tag @@ -95,10 +85,6 @@ 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 */ diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Tue May 27 16:30:26 2008 +0000 @@ -117,8 +117,13 @@ jabber_attention_types, /* attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ +#ifdef USE_FARSIGHT jabber_initiate_media, /* initiate_media */ jabber_can_do_media /* can_do_media */ +#else + NULL, /* initiate_media */ + NULL /* can_do_media */ +#endif }; static gboolean load_plugin(PurplePlugin *plugin) diff -r 903fb6879521 -r 7d2e5f57dbca libpurple/server.c --- a/libpurple/server.c Tue May 27 06:51:21 2008 +0000 +++ b/libpurple/server.c Tue May 27 16:30:26 2008 +0000 @@ -1095,5 +1095,18 @@ return FALSE; } } - -#endif +#else +void * +serv_initiate_media(void *gc, void *who, void *type) +{ + purple_debug_info("serv", "Blank serv_initiate_media called\n"); + return NULL; +} + +void * +serv_can_do_media(void *gc, void *who, void *type) +{ + purple_debug_info("serv", "Blank serv_can_do_media called\n"); + return NULL; +} +#endif /* USE_FARSIGHT */ diff -r 903fb6879521 -r 7d2e5f57dbca pidgin/gtkconv.c --- a/pidgin/gtkconv.c Tue May 27 06:51:21 2008 +0000 +++ b/pidgin/gtkconv.c Tue May 27 16:30:26 2008 +0000 @@ -7652,7 +7652,8 @@ purple_conversation_get_name(conv), PURPLE_MEDIA_RECV_AUDIO & PURPLE_MEDIA_SEND_AUDIO); - purple_media_wait(media); + if (media) + purple_media_wait(media); } static void @@ -7789,9 +7790,10 @@ show_protocol_icons_pref_cb, NULL); purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/conversations/im/hide_new", hide_new_pref_cb, NULL); - +#ifdef USE_FARSIGHT g_signal_connect(G_OBJECT(purple_media_manager_get()), "init-media", G_CALLBACK(pidgin_conv_new_media_cb), NULL); +#endif /********************************************************************** diff -r 903fb6879521 -r 7d2e5f57dbca pidgin/gtkmedia.c --- a/pidgin/gtkmedia.c Tue May 27 06:51:21 2008 +0000 +++ b/pidgin/gtkmedia.c Tue May 27 16:30:26 2008 +0000 @@ -32,8 +32,6 @@ #ifdef USE_FARSIGHT -#include - typedef enum { /* Waiting for response */ @@ -181,37 +179,18 @@ 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; + gdouble rms_db; + gdouble percent; const GValue *list; const GValue *value; - GstElement *src = GST_ELEMENT(message); + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(message)); if (message->type != GST_MESSAGE_ELEMENT) return TRUE; @@ -228,14 +207,52 @@ value = gst_value_list_get_value(list, 0); rms_db = g_value_get_double(value); + percent = pow(10, rms_db / 20) * 5; + + if(percent > 1.0) + percent = 1.0; + 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); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->send_progress), percent); else - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->recv_progress), pow(10, rms_db / 20) * 5); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->recv_progress), percent); return TRUE; } + +static void +pidgin_media_disconnect_levels(PurpleMedia *media, PidginMedia *gtkmedia) +{ + GstElement *element = purple_media_get_audio_pipeline(media); + gulong handler_id = g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), + G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, + NULL, G_CALLBACK(level_message_cb), gtkmedia); + if (handler_id) + g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), + handler_id); +} + +static void +pidgin_media_finalize (GObject *media) +{ + PidginMedia *gtkmedia = PIDGIN_MEDIA(media); + if (gtkmedia->priv->media) { + pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia); + 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 void pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia) { diff -r 903fb6879521 -r 7d2e5f57dbca pidgin/gtkmedia.h --- a/pidgin/gtkmedia.h Tue May 27 06:51:21 2008 +0000 +++ b/pidgin/gtkmedia.h Tue May 27 16:30:26 2008 +0000 @@ -28,7 +28,6 @@ #ifdef USE_FARSIGHT -#include #include #include diff -r 903fb6879521 -r 7d2e5f57dbca pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Tue May 27 06:51:21 2008 +0000 +++ b/pidgin/gtkprefs.c Tue May 27 16:30:26 2008 +0000 @@ -2027,9 +2027,9 @@ GList *ret = NULL; for(; devices ; devices = devices->next) { - gchar *name = purple_media_get_device_name(element, devices->data); + gchar *name = purple_media_get_device_name(GST_ELEMENT(element), devices->data); ret = g_list_append(ret, name); - ret = g_list_append(ret, name); + ret = g_list_append(ret, g_value_dup_string(devices->data)); } return ret; @@ -2037,36 +2037,132 @@ /* * Test functions to run video preview - * (this is not really functional right now...) - * */ +static gboolean +preview_video_bus_call(GstBus *bus, GstMessage *msg, gpointer pipeline) +{ + switch(GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_EOS: + purple_debug_info("preview-video", "End of Stream\n"); + break; + case GST_MESSAGE_ERROR: { + gchar *debug = NULL; + GError *err = NULL; + + gst_message_parse_error(msg, &err, &debug); + + purple_debug_error("preview-video", "Error: %s\n", err->message); + g_error_free(err); + + if (debug) { + purple_debug_error("preview-video", "details: %s\n", debug); + g_free (debug); + } + break; + } + default: + return TRUE; + } + + gst_element_set_state(pipeline, GST_STATE_NULL); + gst_object_unref(GST_PIPELINE(pipeline)); + return TRUE; +} + static void preview_button_clicked(GtkWidget *widget, gpointer *data) { - GstElement *video = (GstElement *) data; + const char *plugin = purple_prefs_get_string("/purple/media/video/plugin"); + const char *device = purple_prefs_get_string("/purple/media/video/device"); + GstBus *bus; /* 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"; + gchar *test_pipeline_str = NULL; + + if (strlen(device) > 0) + test_pipeline_str = g_strdup_printf("%s device=\"%s\" !" \ + " ffmpegcolorspace !" \ + " autovideosink", + plugin, device); + else + test_pipeline_str = g_strdup_printf("%s ! ffmpegcolorspace !" \ + " autovideosink", plugin); + pipeline = gst_parse_launch (test_pipeline_str, &p_err); + g_free(test_pipeline_str); + + bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + gst_bus_add_watch(bus, preview_video_bus_call, pipeline); + gst_object_unref(bus); + gst_element_set_state(pipeline, GST_STATE_PLAYING); } +static void +media_plugin_changed_cb(const gchar *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + GtkWidget *hbox = data; + GtkWidget *dd = NULL; + GtkWidget *preview_button = NULL; + const char *plugin = value; + const char *device = purple_prefs_get_string("/purple/media/video/device"); + GstElement *video = purple_media_get_element(plugin); + GList *video_devices = purple_media_get_devices(video); + GList *video_items = get_device_items(video, video_devices); + GList *list; + g_list_free(video_devices); + + if (video_items == NULL) { + video_items = g_list_prepend(video_items, g_strdup("")); + video_items = g_list_prepend(video_items, g_strdup("Default")); + } + + if (g_list_find(video_items, device) == NULL) + { + purple_prefs_set_string("/purple/media/video/device", + g_list_next(video_items)->data); + } + + list = gtk_container_get_children(GTK_CONTAINER(hbox)); + + while (list) { + gtk_widget_destroy(list->data); + list = g_list_delete_link(list, list); + } + + dd = pidgin_prefs_dropdown_from_list(hbox, _("_Device:"), PURPLE_PREF_STRING, + "/purple/media/video/device", + video_items); + + 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(GTK_CONTAINER(hbox), preview_button); + + gtk_widget_show_all(hbox); +} + static GtkWidget * media_page() { GtkWidget *ret; - GtkWidget *sg; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *dd; GtkWidget *preview_button; - - GstElement *video = purple_media_get_element("v4lsrc"); + GtkSizeGroup *sg; + const char *plugin = purple_prefs_get_string("/purple/media/video/plugin"); + const char *device = purple_prefs_get_string("/purple/media/video/device"); + + GstElement *video = purple_media_get_element(plugin); GstElement *audio = purple_media_get_element("alsasrc"); GList *video_devices = purple_media_get_devices(video); @@ -2078,17 +2174,46 @@ g_list_free(video_devices); g_list_free(audio_devices); + if (video_items == NULL) { + video_items = g_list_prepend(video_items, ""); + video_items = g_list_prepend(video_items, "Default"); + } + + if (g_list_find(video_items, device) == NULL) + { + purple_prefs_set_string("/purple/media/video/device", + g_list_next(video_items)->data); + } + 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(vbox, _("_Plugin:"), PURPLE_PREF_STRING, + "/purple/media/video/plugin", + _("Default"), "gconfvideosrc", + _("Video4Linux"), "v4lsrc", + _("Video4Linux2"), "v4l2src", + NULL); + + 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); dd = pidgin_prefs_dropdown_from_list(hbox, _("_Device:"), PURPLE_PREF_STRING, "/purple/media/video/device", video_items); + purple_prefs_connect_callback(prefs, "/purple/media/video/plugin", + media_plugin_changed_cb, hbox); + + g_signal_connect_swapped(hbox, "destroy", + G_CALLBACK(purple_prefs_disconnect_by_handle), hbox); + gtk_size_group_add_widget(sg, dd); gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5); @@ -2096,8 +2221,8 @@ 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); + gtk_container_add(GTK_CONTAINER(hbox), preview_button); + gtk_container_add(GTK_CONTAINER(vbox), hbox); vbox = pidgin_make_frame (ret, _("Audio Input")); dd = pidgin_prefs_dropdown_from_list(vbox, _("_Device:"), PURPLE_PREF_STRING, @@ -2242,7 +2367,7 @@ prefs_notebook_add_page(_("Smiley Themes"), theme_page(), notebook_page++); prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++); -#if USE_FARSIGHT +#ifdef USE_FARSIGHT prefs_notebook_add_page(_("Media"), media_page(), notebook_page++); #endif prefs_notebook_add_page(_("Network"), network_page(), notebook_page++); @@ -2373,6 +2498,7 @@ #ifdef USE_FARSIGHT purple_prefs_add_none("/purple/media"); purple_prefs_add_none("/purple/media/video"); + purple_prefs_add_string("/purple/media/video/plugin", "gconfvideosrc"); purple_prefs_add_string("/purple/media/video/device", ""); purple_prefs_add_none("/purple/media/audio"); purple_prefs_add_string("/purple/media/audio/device", "");