diff --git a/pidgin-2.10.11-add-dtmf-support.patch b/pidgin-2.10.11-add-dtmf-support.patch new file mode 100644 index 0000000..b7d7506 --- /dev/null +++ b/pidgin-2.10.11-add-dtmf-support.patch @@ -0,0 +1,350 @@ + +# HG changeset patch +# User David Woodhouse +# Date 1425675783 0 +# Node ID 6b4576edf2a694ab55d0d06d3643c44601a75b15 +# Parent 714ba418d0aa5ba0cc4cc3b9db37296cd2bbf041 +Add out-of-band DTMF support and dialpad to use it + +This is a backport of e4c122196b08 from the trunk. It adds the UI and +farstream backend support for sending DTMF. + +Fixes #15575 + +diff --git a/libpurple/media.c b/libpurple/media.c +--- a/libpurple/media.c ++++ b/libpurple/media.c +@@ -1439,3 +1439,46 @@ + } + #endif /* USE_GSTREAMER */ + ++gboolean ++purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id, ++ gchar dtmf, guint8 volume, guint16 duration) ++{ ++#ifdef USE_VV ++ PurpleAccount *account = NULL; ++ PurpleConnection *gc = NULL; ++ PurplePlugin *prpl = NULL; ++ PurplePluginProtocolInfo *prpl_info = NULL; ++ PurpleMediaBackendIface *backend_iface = NULL; ++ ++ if (media) ++ { ++ account = purple_media_get_account(media); ++ backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend); ++ } ++ if (account) ++ gc = purple_account_get_connection(account); ++ if (gc) ++ prpl = purple_connection_get_prpl(gc); ++ if (prpl) ++ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); ++ ++ if (dtmf == 'a') ++ dtmf = 'A'; ++ else if (dtmf == 'b') ++ dtmf = 'B'; ++ else if (dtmf == 'c') ++ dtmf = 'C'; ++ else if (dtmf == 'd') ++ dtmf = 'D'; ++ ++ g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE); ++ ++ if (backend_iface && backend_iface->send_dtmf ++ && backend_iface->send_dtmf(media->priv->backend, ++ session_id, dtmf, volume, duration)) ++ { ++ return TRUE; ++ } ++#endif ++ return FALSE; ++} +diff --git a/libpurple/media.h b/libpurple/media.h +--- a/libpurple/media.h ++++ b/libpurple/media.h +@@ -437,6 +437,21 @@ + */ + void purple_media_remove_output_windows(PurpleMedia *media); + ++/** ++ * Sends a DTMF signal out-of-band. ++ * ++ * @param media The media instance to send a DTMF signal to. ++ * @param sess_id The session id of the session to send the DTMF signal on. ++ * @param dtmf The character representing the DTMF in the range [0-9#*A-D]. ++ * @param volume The power level expressed in dBm0 after dropping the sign ++ * in the range of 0 to 63. A larger value represents a lower volume. ++ * @param duration The duration of the tone in milliseconds. ++ * ++ * @since 2.11 ++ */ ++gboolean purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id, ++ gchar dtmf, guint8 volume, guint16 duration); ++ + #ifdef __cplusplus + } + #endif +diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c +--- a/libpurple/media/backend-fs2.c ++++ b/libpurple/media/backend-fs2.c +@@ -94,6 +94,9 @@ + static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self, + guint num_params, GParameter *params); + static const gchar **purple_media_backend_fs2_get_available_params(void); ++static gboolean purple_media_backend_fs2_send_dtmf( ++ PurpleMediaBackend *self, const gchar *sess_id, ++ gchar dtmf, guint8 volume, guint16 duration); + + static void free_stream(PurpleMediaBackendFs2Stream *stream); + static void free_session(PurpleMediaBackendFs2Session *session); +@@ -499,6 +502,7 @@ + iface->set_send_codec = purple_media_backend_fs2_set_send_codec; + iface->set_params = purple_media_backend_fs2_set_params; + iface->get_available_params = purple_media_backend_fs2_get_available_params; ++ iface->send_dtmf = purple_media_backend_fs2_send_dtmf; + } + + static FsMediaType +@@ -2436,6 +2440,65 @@ + + return supported_params; + } ++static gboolean ++send_dtmf_callback(gpointer userdata) ++{ ++ FsSession *session = userdata; ++ ++ fs_session_stop_telephony_event(session); ++ ++ return FALSE; ++} ++static gboolean ++purple_media_backend_fs2_send_dtmf(PurpleMediaBackend *self, ++ const gchar *sess_id, gchar dtmf, guint8 volume, ++ guint16 duration) ++{ ++ PurpleMediaBackendFs2Session *session; ++ FsDTMFEvent event; ++ ++ g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); ++ ++ session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id); ++ if (session == NULL) ++ return FALSE; ++ ++ /* Convert DTMF char into FsDTMFEvent enum */ ++ switch(dtmf) { ++ case '0': event = FS_DTMF_EVENT_0; break; ++ case '1': event = FS_DTMF_EVENT_1; break; ++ case '2': event = FS_DTMF_EVENT_2; break; ++ case '3': event = FS_DTMF_EVENT_3; break; ++ case '4': event = FS_DTMF_EVENT_4; break; ++ case '5': event = FS_DTMF_EVENT_5; break; ++ case '6': event = FS_DTMF_EVENT_6; break; ++ case '7': event = FS_DTMF_EVENT_7; break; ++ case '8': event = FS_DTMF_EVENT_8; break; ++ case '9': event = FS_DTMF_EVENT_9; break; ++ case '*': event = FS_DTMF_EVENT_STAR; break; ++ case '#': event = FS_DTMF_EVENT_POUND; break; ++ case 'A': event = FS_DTMF_EVENT_A; break; ++ case 'B': event = FS_DTMF_EVENT_B; break; ++ case 'C': event = FS_DTMF_EVENT_C; break; ++ case 'D': event = FS_DTMF_EVENT_D; break; ++ default: ++ return FALSE; ++ } ++ ++ if (!fs_session_start_telephony_event(session->session, ++ event, volume)) { ++ return FALSE; ++ } ++ ++ if (duration <= 50) { ++ fs_session_stop_telephony_event(session->session); ++ } else { ++ purple_timeout_add(duration, send_dtmf_callback, ++ session->session); ++ } ++ ++ return TRUE; ++} + #else + GType + purple_media_backend_fs2_get_type(void) +diff --git a/libpurple/media/backend-iface.h b/libpurple/media/backend-iface.h +--- a/libpurple/media/backend-iface.h ++++ b/libpurple/media/backend-iface.h +@@ -71,6 +71,9 @@ + void (*set_params) (PurpleMediaBackend *self, + guint num_params, GParameter *params); + const gchar **(*get_available_params) (void); ++ gboolean (*send_dtmf) (PurpleMediaBackend *self, ++ const gchar *sess_id, gchar dtmf, guint8 volume, ++ guint16 duration); + }; + + /** +diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c +--- a/pidgin/gtkmedia.c ++++ b/pidgin/gtkmedia.c +@@ -41,6 +41,7 @@ + #ifdef _WIN32 + #include + #endif ++#include + + #include + +@@ -759,6 +760,136 @@ + } + + static void ++phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data) ++{ ++ PidginMedia *gtkmedia = user_data; ++ gint num; ++ gchar *sid; ++ ++ num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit")); ++ sid = g_object_get_data(G_OBJECT(button), "session-id"); ++ ++ purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50); ++} ++ ++static inline GtkWidget * ++phone_create_button(const gchar *text_hi, const gchar *text_lo) ++{ ++ GtkWidget *button; ++ GtkWidget *label_hi; ++ GtkWidget *label_lo; ++ GtkWidget *grid; ++ const gchar *text_hi_local; ++ ++ if (text_hi) ++ text_hi_local = _(text_hi); ++ else ++ text_hi_local = ""; ++ ++ grid = gtk_vbox_new(TRUE, 0); ++ ++ button = gtk_button_new(); ++ label_hi = gtk_label_new(text_hi_local); ++ gtk_misc_set_alignment(GTK_MISC(label_hi), 0.5, 0.5); ++ gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0); ++ label_lo = gtk_label_new(text_lo); ++ gtk_misc_set_alignment(GTK_MISC(label_lo), 0.5, 0.5); ++ gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE); ++ gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0); ++ gtk_container_add(GTK_CONTAINER(button), grid); ++ ++ return button; ++} ++ ++static struct phone_label { ++ gchar *subtext; ++ gchar *text; ++ gchar chr; ++} phone_labels[] = { ++ {"1", NULL, '1'}, ++ /* Translators note: These are the letters on the keys of a numeric ++ keypad; translate according to ยง7.2.4 of ++ http://www.etsi.org/deliver/etsi_es/202100_202199/202130/01.01.01_60/es_20213 */ ++ /* Letters on the '2' key of a numeric keypad */ ++ {"2", N_("ABC"), '2'}, ++ /* Letters on the '3' key of a numeric keypad */ ++ {"3", N_("DEF"), '3'}, ++ /* Letters on the '4' key of a numeric keypad */ ++ {"4", N_("GHI"), '4'}, ++ /* Letters on the '5' key of a numeric keypad */ ++ {"5", N_("JKL"), '5'}, ++ /* Letters on the '6' key of a numeric keypad */ ++ {"6", N_("MNO"), '6'}, ++ /* Letters on the '7' key of a numeric keypad */ ++ {"7", N_("PQRS"), '7'}, ++ /* Letters on the '8' key of a numeric keypad */ ++ {"8", N_("TUV"), '8'}, ++ /* Letters on the '9' key of a numeric keypad */ ++ {"9", N_("WXYZ"), '9'}, ++ {"*", NULL, '*'}, ++ {"0", NULL, '0'}, ++ {"#", NULL, '#'}, ++ {NULL, NULL, 0} ++}; ++ ++static gboolean ++pidgin_media_dtmf_key_press_event_cb(GtkWidget *widget, ++ GdkEvent *event, gpointer user_data) ++{ ++ PidginMedia *gtkmedia = user_data; ++ GdkEventKey *key = (GdkEventKey *) event; ++ ++ if (event->type != GDK_KEY_PRESS) { ++ return FALSE; ++ } ++ ++ if ((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) || ++ key->keyval == GDK_KEY_asterisk || ++ key->keyval == GDK_KEY_numbersign) { ++ gchar *sid = g_object_get_data(G_OBJECT(widget), "session-id"); ++ ++ purple_media_send_dtmf(gtkmedia->priv->media, sid, key->keyval, 25, 50); ++ } ++ ++ return FALSE; ++} ++ ++static GtkWidget * ++pidgin_media_add_dtmf_widget(PidginMedia *gtkmedia, ++ PurpleMediaSessionType type, const gchar *_sid) ++{ ++ GtkWidget *grid = gtk_table_new(4, 3, TRUE); ++ GtkWidget *button; ++ gint index = 0; ++ GtkWindow *win = >kmedia->parent; ++ ++ /* Add buttons */ ++ for (index = 0; phone_labels[index].subtext != NULL; index++) { ++ button = phone_create_button(phone_labels[index].text, ++ phone_labels[index].subtext); ++ g_signal_connect(button, "pressed", ++ G_CALLBACK(phone_dtmf_pressed_cb), gtkmedia); ++ g_object_set_data(G_OBJECT(button), "dtmf-digit", ++ GINT_TO_POINTER(phone_labels[index].chr)); ++ g_object_set_data_full(G_OBJECT(button), "session-id", ++ g_strdup(_sid), g_free); ++ gtk_table_attach(GTK_TABLE(grid), button, index % 3, ++ index % 3 + 1, index / 3, index / 3 + 1, ++ GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, ++ 2, 2); ++ } ++ ++ g_signal_connect(G_OBJECT(win), "key-press-event", ++ G_CALLBACK(pidgin_media_dtmf_key_press_event_cb), gtkmedia); ++ g_object_set_data_full(G_OBJECT(win), "session-id", ++ g_strdup(_sid), g_free); ++ ++ gtk_widget_show_all(grid); ++ ++ return grid; ++} ++ ++static void + pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid) + { + GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL; +@@ -888,7 +1019,11 @@ + + gtk_box_pack_end(GTK_BOX(recv_widget), + pidgin_media_add_audio_widget(gtkmedia, +- PURPLE_MEDIA_SEND_AUDIO, NULL), FALSE, FALSE, 0); ++ PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0); ++ ++ gtk_box_pack_end(GTK_BOX(recv_widget), ++ pidgin_media_add_dtmf_widget(gtkmedia, ++ PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0); + } + + if (type & PURPLE_MEDIA_AUDIO && + diff --git a/pidgin-2.10.11-application-media.patch b/pidgin-2.10.11-application-media.patch new file mode 100644 index 0000000..3cc66ca --- /dev/null +++ b/pidgin-2.10.11-application-media.patch @@ -0,0 +1,1137 @@ + +# HG changeset patch +# User Youness Alaoui +# Date 1405979621 14400 +# Node ID 4fe1034f3dce1c5cd3c929ab8c58db8e27655beb +# Parent d729a9b2126594df3e38647e926ac7c0a7db807b +Add application media type and APIs + +Fixes #16315 + +Index: pidgin-2.10.11/configure.ac +=================================================================== +--- pidgin-2.10.11.orig/configure.ac ++++ pidgin-2.10.11/configure.ac +@@ -916,6 +916,20 @@ fi + AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno") + + dnl ####################################################################### ++dnl # Check for Raw data streams support in Farstream ++dnl ####################################################################### ++if test "x$enable_vv" != "xno" -a "x$with_gstreamer" == "x1.0"; then ++ AC_MSG_CHECKING(for raw data support in Farstream) ++ PKG_CHECK_MODULES(GSTAPP, [gstreamer-app-1.0], [ ++ AC_DEFINE(USE_GSTAPP, 1, [Use GStreamer Video Overlay support]) ++ AC_SUBST(GSTAPP_CFLAGS) ++ AC_SUBST(GSTAPP_LIBS) ++ AC_DEFINE(HAVE_MEDIA_APPLICATION, 1, [Define if we have support for application media type.]) ++ AC_MSG_RESULT(yes) ++ ], [AC_MSG_RESULT(no)]) ++fi ++ ++dnl ####################################################################### + dnl # Check for Internationalized Domain Name support + dnl ####################################################################### + +Index: pidgin-2.10.11/libpurple/Makefile.am +=================================================================== +--- pidgin-2.10.11.orig/libpurple/Makefile.am ++++ pidgin-2.10.11/libpurple/Makefile.am +@@ -314,6 +314,7 @@ libpurple_la_LIBADD = \ + $(FARSTREAM_LIBS) \ + $(GSTREAMER_LIBS) \ + $(GSTVIDEO_LIBS) \ ++ $(GSTAPP_LIBS) \ + $(GSTINTERFACES_LIBS) \ + $(IDN_LIBS) \ + ciphers/libpurple-ciphers.la \ +@@ -331,6 +332,7 @@ AM_CPPFLAGS = \ + $(FARSTREAM_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(GSTVIDEO_CFLAGS) \ ++ $(GSTAPP_CFLAGS) \ + $(GSTINTERFACES_CFLAGS) \ + $(IDN_CFLAGS) \ + $(NETWORKMANAGER_CFLAGS) \ +Index: pidgin-2.10.11/libpurple/media-gst.h +=================================================================== +--- pidgin-2.10.11.orig/libpurple/media-gst.h ++++ pidgin-2.10.11/libpurple/media-gst.h +@@ -71,6 +71,7 @@ typedef enum { + + PURPLE_MEDIA_ELEMENT_SRC = 1 << 9, /** can be set as an active src */ + PURPLE_MEDIA_ELEMENT_SINK = 1 << 10, /** can be set as an active sink */ ++ PURPLE_MEDIA_ELEMENT_APPLICATION = 1 << 11, /** supports application data */ + } PurpleMediaElementType; + + #ifdef __cplusplus +Index: pidgin-2.10.11/libpurple/media/backend-fs2.c +=================================================================== +--- pidgin-2.10.11.orig/libpurple/media/backend-fs2.c ++++ pidgin-2.10.11/libpurple/media/backend-fs2.c +@@ -543,6 +543,10 @@ session_type_to_fs_media_type(PurpleMedi + return FS_MEDIA_TYPE_AUDIO; + else if (type & PURPLE_MEDIA_VIDEO) + return FS_MEDIA_TYPE_VIDEO; ++#ifdef HAVE_MEDIA_APPLICATION ++ else if (type & PURPLE_MEDIA_APPLICATION) ++ return FS_MEDIA_TYPE_APPLICATION; ++#endif + else + return 0; + } +@@ -551,7 +555,7 @@ static FsStreamDirection + session_type_to_fs_stream_direction(PurpleMediaSessionType type) + { + if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO || +- (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO) ++ (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO) + return FS_DIRECTION_BOTH; + else if ((type & PURPLE_MEDIA_SEND_AUDIO) || + (type & PURPLE_MEDIA_SEND_VIDEO)) +@@ -559,6 +563,14 @@ session_type_to_fs_stream_direction(Purp + else if ((type & PURPLE_MEDIA_RECV_AUDIO) || + (type & PURPLE_MEDIA_RECV_VIDEO)) + return FS_DIRECTION_RECV; ++#ifdef HAVE_MEDIA_APPLICATION ++ else if ((type & PURPLE_MEDIA_APPLICATION) == PURPLE_MEDIA_APPLICATION) ++ return FS_DIRECTION_BOTH; ++ else if (type & PURPLE_MEDIA_SEND_APPLICATION) ++ return FS_DIRECTION_SEND; ++ else if (type & PURPLE_MEDIA_RECV_APPLICATION) ++ return FS_DIRECTION_RECV; ++#endif + else + return FS_DIRECTION_NONE; + } +@@ -577,6 +589,13 @@ session_type_from_fs(FsMediaType type, F + result |= PURPLE_MEDIA_SEND_VIDEO; + if (direction & FS_DIRECTION_RECV) + result |= PURPLE_MEDIA_RECV_VIDEO; ++#ifdef HAVE_MEDIA_APPLICATION ++ } else if (type == FS_MEDIA_TYPE_APPLICATION) { ++ if (direction & FS_DIRECTION_SEND) ++ result |= PURPLE_MEDIA_SEND_APPLICATION; ++ if (direction & FS_DIRECTION_RECV) ++ result |= PURPLE_MEDIA_RECV_APPLICATION; ++#endif + } + return result; + } +@@ -1333,7 +1352,8 @@ gst_handle_message_error(GstBus *bus, Gs + & PURPLE_MEDIA_AUDIO) + purple_media_error(priv->media, + _("Error with your microphone")); +- else ++ else if (purple_media_get_session_type(priv->media, ++ sessions->data) & PURPLE_MEDIA_VIDEO) + purple_media_error(priv->media, + _("Error with your webcam")); + +@@ -1756,6 +1776,21 @@ create_session(PurpleMediaBackendFs2 *se + session->session = fs_conference_new_session(priv->conference, + session_type_to_fs_media_type(type), &err); + ++#ifdef HAVE_MEDIA_APPLICATION ++ if (type == PURPLE_MEDIA_APPLICATION) { ++ GstCaps *caps; ++ GObject *rtpsession = NULL; ++ ++ caps = gst_caps_new_empty_simple ("application/octet-stream"); ++ fs_session_set_allowed_caps (session->session, caps, caps, NULL); ++ gst_caps_unref (caps); ++ g_object_get (session->session, "internal-session", &rtpsession, NULL); ++ if (rtpsession) { ++ g_object_set (rtpsession, "probation", 0, NULL); ++ g_object_unref (rtpsession); ++ } ++ } ++#endif + if (err != NULL) { + purple_media_error(priv->media, + _("Error creating session: %s"), +@@ -1970,6 +2005,21 @@ src_pad_added_cb(FsStream *fsstream, Gst + gst_bin_add(GST_BIN(priv->confbin), sink); + gst_element_set_state(sink, GST_STATE_PLAYING); + stream->fakesink = sink; ++#ifdef HAVE_MEDIA_APPLICATION ++ } else if (codec->media_type == FS_MEDIA_TYPE_APPLICATION) { ++#if GST_CHECK_VERSION(1,0,0) ++ stream->src = gst_element_factory_make("funnel", NULL); ++#else ++ stream->src = gst_element_factory_make("fsfunnel", NULL); ++#endif ++ sink = purple_media_manager_get_element( ++ purple_media_get_manager(priv->media), ++ PURPLE_MEDIA_RECV_APPLICATION, priv->media, ++ stream->session->id, ++ stream->participant); ++ gst_bin_add(GST_BIN(priv->confbin), sink); ++ gst_element_set_state(sink, GST_STATE_PLAYING); ++#endif + } + stream->tee = gst_element_factory_make("tee", NULL); + gst_bin_add_many(GST_BIN(priv->confbin), +@@ -2332,6 +2382,9 @@ purple_media_backend_fs2_codecs_ready(Pu + return FALSE; + + if (session->type & (PURPLE_MEDIA_SEND_AUDIO | ++#ifdef HAVE_MEDIA_APPLICATION ++ PURPLE_MEDIA_SEND_APPLICATION | ++#endif + PURPLE_MEDIA_SEND_VIDEO)) { + #ifdef HAVE_FARSIGHT + g_object_get(session->session, +@@ -2355,6 +2408,9 @@ purple_media_backend_fs2_codecs_ready(Pu + PurpleMediaBackendFs2Session *session = values->data; + + if (session->type & (PURPLE_MEDIA_SEND_AUDIO | ++#ifdef HAVE_MEDIA_APPLICATION ++ PURPLE_MEDIA_SEND_APPLICATION | ++#endif + PURPLE_MEDIA_SEND_VIDEO)) { + #ifdef HAVE_FARSIGHT + g_object_get(session->session, +Index: pidgin-2.10.11/libpurple/media/codec.c +=================================================================== +--- pidgin-2.10.11.orig/libpurple/media/codec.c ++++ pidgin-2.10.11/libpurple/media/codec.c +@@ -188,7 +188,7 @@ purple_media_codec_class_init(PurpleMedi + g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE, + g_param_spec_flags("media-type", + "Media Type", +- "Whether this is an audio of video codec.", ++ "Whether this is an audio, video or application codec.", + PURPLE_TYPE_MEDIA_SESSION_TYPE, + PURPLE_MEDIA_NONE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); +@@ -402,6 +402,8 @@ purple_media_codec_to_string(const Purpl + media_type_str = "audio"; + else if (priv->media_type & PURPLE_MEDIA_VIDEO) + media_type_str = "video"; ++ else if (priv->media_type & PURPLE_MEDIA_APPLICATION) ++ media_type_str = "application"; + + g_string_printf(string, "%d: %s %s clock:%d channels:%d", priv->id, + media_type_str, priv->encoding_name, +Index: pidgin-2.10.11/libpurple/media/enum-types.c +=================================================================== +--- pidgin-2.10.11.orig/libpurple/media/enum-types.c ++++ pidgin-2.10.11/libpurple/media/enum-types.c +@@ -176,10 +176,16 @@ purple_media_session_type_get_type() + "PURPLE_MEDIA_RECV_VIDEO", "recv-video" }, + { PURPLE_MEDIA_SEND_VIDEO, + "PURPLE_MEDIA_SEND_VIDEO", "send-video" }, ++ { PURPLE_MEDIA_RECV_APPLICATION, ++ "PURPLE_MEDIA_RECV_APPLICATION", "recv-application" }, ++ { PURPLE_MEDIA_SEND_APPLICATION, ++ "PURPLE_MEDIA_SEND_APPLICATION", "send-application" }, + { PURPLE_MEDIA_AUDIO, + "PURPLE_MEDIA_AUDIO", "audio" }, + { PURPLE_MEDIA_VIDEO, + "PURPLE_MEDIA_VIDEO", "video" }, ++ { PURPLE_MEDIA_APPLICATION, ++ "PURPLE_MEDIA_APPLICATION", "application" }, + { 0, NULL, NULL } + }; + type = g_flags_register_static( +Index: pidgin-2.10.11/libpurple/media/enum-types.h +=================================================================== +--- pidgin-2.10.11.orig/libpurple/media/enum-types.h ++++ pidgin-2.10.11/libpurple/media/enum-types.h +@@ -92,8 +92,12 @@ typedef enum { + PURPLE_MEDIA_SEND_AUDIO = 1 << 1, + PURPLE_MEDIA_RECV_VIDEO = 1 << 2, + PURPLE_MEDIA_SEND_VIDEO = 1 << 3, ++ PURPLE_MEDIA_RECV_APPLICATION = 1 << 4, ++ PURPLE_MEDIA_SEND_APPLICATION = 1 << 5, + PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO, +- PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO ++ PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO, ++ PURPLE_MEDIA_APPLICATION = PURPLE_MEDIA_RECV_APPLICATION | ++ PURPLE_MEDIA_SEND_APPLICATION + } PurpleMediaSessionType; + + /** Media state-changed types */ +Index: pidgin-2.10.11/libpurple/mediamanager.c +=================================================================== +--- pidgin-2.10.11.orig/libpurple/mediamanager.c ++++ pidgin-2.10.11/libpurple/mediamanager.c +@@ -44,6 +44,9 @@ + #else + #include + #endif ++#ifdef HAVE_MEDIA_APPLICATION ++#include ++#endif + + #if GST_CHECK_VERSION(1,0,0) + #include +@@ -97,14 +100,45 @@ struct _PurpleMediaManagerPrivate + PurpleMediaElementInfo *video_sink; + PurpleMediaElementInfo *audio_src; + PurpleMediaElementInfo *audio_sink; ++ ++#ifdef HAVE_MEDIA_APPLICATION ++ /* Application data streams */ ++ GList *appdata_info; /* holds PurpleMediaAppDataInfo */ ++ GMutex appdata_mutex; ++#endif + }; + ++#ifdef HAVE_MEDIA_APPLICATION ++typedef struct { ++ PurpleMedia *media; ++ GWeakRef media_ref; ++ gchar *session_id; ++ gchar *participant; ++ PurpleMediaAppDataCallbacks callbacks; ++ gpointer user_data; ++ GDestroyNotify notify; ++ GstAppSrc *appsrc; ++ GstAppSink *appsink; ++ gint num_samples; ++ GstSample *current_sample; ++ guint sample_offset; ++ gboolean writable; ++ gboolean connected; ++ guint writable_timer_id; ++ guint readable_timer_id; ++ GCond readable_cond; ++} PurpleMediaAppDataInfo; ++#endif ++ + #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate)) + #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate)) + + static void purple_media_manager_class_init (PurpleMediaManagerClass *klass); + static void purple_media_manager_init (PurpleMediaManager *media); + static void purple_media_manager_finalize (GObject *object); ++#ifdef HAVE_MEDIA_APPLICATION ++static void free_appdata_info_locked (PurpleMediaAppDataInfo *info); ++#endif + + static GObjectClass *parent_class = NULL; + +@@ -190,8 +224,10 @@ purple_media_manager_init (PurpleMediaMa + media->priv->medias = NULL; + media->priv->private_medias = NULL; + media->priv->next_output_window_id = 1; +-#ifdef USE_VV + media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2; ++#ifdef HAVE_MEDIA_APPLICATION ++ media->priv->appdata_info = NULL; ++ g_mutex_init (&media->priv->appdata_mutex); + #endif + + purple_prefs_add_none("/purple/media"); +@@ -220,6 +256,13 @@ purple_media_manager_finalize (GObject * + } + if (priv->video_caps) + gst_caps_unref(priv->video_caps); ++#ifdef HAVE_MEDIA_APPLICATION ++ if (priv->appdata_info) ++ g_list_free_full (priv->appdata_info, ++ (GDestroyNotify) free_appdata_info_locked); ++ g_mutex_clear (&priv->appdata_mutex); ++#endif ++ + parent_class->finalize(media); + } + #endif +@@ -440,8 +483,23 @@ purple_media_manager_remove_media(Purple + medias = &manager->priv->private_medias; + } + +- if (list) ++ if (list) { + *medias = g_list_delete_link(*medias, list); ++ ++#ifdef HAVE_MEDIA_APPLICATION ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ for (list = manager->priv->appdata_info; list; list = list->next) { ++ PurpleMediaAppDataInfo *info = list->data; ++ ++ if (info->media == media) { ++ manager->priv->appdata_info = g_list_delete_link ( ++ manager->priv->appdata_info, list); ++ free_appdata_info_locked (info); ++ } ++ } ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++#endif ++ } + #endif + } + +@@ -493,6 +551,92 @@ purple_media_manager_get_private_media_b + return get_media_by_account (manager, account, TRUE); + } + ++#ifdef HAVE_MEDIA_APPLICATION ++static void ++free_appdata_info_locked (PurpleMediaAppDataInfo *info) ++{ ++ if (info->notify) ++ info->notify (info->user_data); ++ ++ /* Make sure no other thread is using the structure */ ++ g_free (info->session_id); ++ g_free (info->participant); ++ ++ if (info->readable_timer_id) { ++ purple_timeout_remove (info->readable_timer_id); ++ info->readable_timer_id = 0; ++ } ++ ++ if (info->writable_timer_id) { ++ purple_timeout_remove (info->writable_timer_id); ++ info->writable_timer_id = 0; ++ } ++ ++ if (info->current_sample) ++ gst_sample_unref (info->current_sample); ++ info->current_sample = NULL; ++ ++ /* Unblock any reading thread before destroying the GCond */ ++ g_cond_broadcast (&info->readable_cond); ++ ++ g_cond_clear (&info->readable_cond); ++ ++ g_slice_free (PurpleMediaAppDataInfo, info); ++} ++ ++/* ++ * Get an app data info struct associated with a session and lock the mutex ++ * We don't want to return an info struct and unlock then it gets destroyed ++ * so we need to return it with the lock still taken ++ */ ++static PurpleMediaAppDataInfo * ++get_app_data_info_and_lock (PurpleMediaManager *manager, ++ PurpleMedia *media, const gchar *session_id, const gchar *participant) ++{ ++ GList *i; ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ for (i = manager->priv->appdata_info; i; i = i->next) { ++ PurpleMediaAppDataInfo *info = i->data; ++ ++ if (info->media == media && ++ g_strcmp0 (info->session_id, session_id) == 0 && ++ (participant == NULL || ++ g_strcmp0 (info->participant, participant) == 0)) { ++ return info; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Get an app data info struct associated with a session and lock the mutex ++ * if it doesn't exist, we create it. ++ */ ++static PurpleMediaAppDataInfo * ++ensure_app_data_info_and_lock (PurpleMediaManager *manager, PurpleMedia *media, ++ const gchar *session_id, const gchar *participant) ++{ ++ PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager, media, ++ session_id, participant); ++ ++ if (info == NULL) { ++ info = g_slice_new0 (PurpleMediaAppDataInfo); ++ info->media = media; ++ g_weak_ref_init (&info->media_ref, media); ++ info->session_id = g_strdup (session_id); ++ info->participant = g_strdup (participant); ++ g_cond_init (&info->readable_cond); ++ manager->priv->appdata_info = g_list_prepend ( ++ manager->priv->appdata_info, info); ++ } ++ ++ return info; ++} ++#endif ++ ++ + #ifdef USE_VV + static void + request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data) +@@ -577,6 +721,351 @@ purple_media_manager_get_video_caps(Purp + #endif + } + ++#ifdef HAVE_MEDIA_APPLICATION ++/* ++ * Calls the appdata writable callback from the main thread. ++ * This needs to grab the appdata lock and make sure it didn't get destroyed ++ * before calling the callback. ++ */ ++static gboolean ++appsrc_writable (gpointer user_data) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ PurpleMediaAppDataInfo *info = user_data; ++ void (*writable_cb) (PurpleMediaManager *manager, PurpleMedia *media, ++ const gchar *session_id, const gchar *participant, gboolean writable, ++ gpointer user_data); ++ PurpleMedia *media; ++ gchar *session_id; ++ gchar *participant; ++ gboolean writable; ++ gpointer cb_data; ++ guint *timer_id_ptr = &info->writable_timer_id; ++ guint timer_id = *timer_id_ptr; ++ ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ if (timer_id == 0 || timer_id != *timer_id_ptr) { ++ /* In case info was freed while we were waiting for the mutex to unlock ++ * we still have a pointer to the timer_id which should still be ++ * accessible since it's in the Glib slice allocator. It gets set to 0 ++ * just after the timeout is canceled which happens also before the ++ * AppDataInfo is freed, so even if that memory slice gets reused, the ++ * timer_id would be different from its previous value (unless ++ * extremely unlucky). So checking if the value for the timer_id changed ++ * should be enough to prevent any kind of race condition in which the ++ * media/AppDataInfo gets destroyed in one thread while the timeout was ++ * triggered and is waiting on the mutex to get unlocked in this thread ++ */ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return FALSE; ++ } ++ writable_cb = info->callbacks.writable; ++ media = g_weak_ref_get (&info->media_ref); ++ session_id = g_strdup (info->session_id); ++ participant = g_strdup (info->participant); ++ writable = info->writable && info->connected; ++ cb_data = info->user_data; ++ ++ info->writable_timer_id = 0; ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ ++ ++ if (writable_cb && media) ++ writable_cb (manager, media, session_id, participant, writable, ++ cb_data); ++ ++ g_object_unref (media); ++ g_free (session_id); ++ g_free (participant); ++ ++ return FALSE; ++} ++ ++/* ++ * Schedule a writable callback to be called from the main thread. ++ * We need to do this because need-data/enough-data signals from appsrc ++ * will come from the streaming thread and we need to create ++ * a source that we attach to the main context but we can't use ++ * g_main_context_invoke since we need to be able to cancel the source if the ++ * media gets destroyed. ++ * We use a timeout source instead of idle source, so the callback gets a higher ++ * priority ++ */ ++static void ++call_appsrc_writable_locked (PurpleMediaAppDataInfo *info) ++{ ++ /* We already have a writable callback scheduled, don't create another one */ ++ if (info->writable_timer_id || info->callbacks.writable == NULL) ++ return; ++ ++ info->writable_timer_id = purple_timeout_add (0, appsrc_writable, info); ++} ++ ++static void ++appsrc_need_data (GstAppSrc *appsrc, guint length, gpointer user_data) ++{ ++ PurpleMediaAppDataInfo *info = user_data; ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ if (!info->writable) { ++ info->writable = TRUE; ++ /* Only signal writable if we also established a connection */ ++ if (info->connected) ++ call_appsrc_writable_locked (info); ++ } ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++} ++ ++static void ++appsrc_enough_data (GstAppSrc *appsrc, gpointer user_data) ++{ ++ PurpleMediaAppDataInfo *info = user_data; ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ if (info->writable) { ++ info->writable = FALSE; ++ call_appsrc_writable_locked (info); ++ } ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++} ++ ++static gboolean ++appsrc_seek_data (GstAppSrc *appsrc, guint64 offset, gpointer user_data) ++{ ++ return FALSE; ++} ++ ++static void ++appsrc_destroyed (PurpleMediaAppDataInfo *info) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ info->appsrc = NULL; ++ if (info->writable) { ++ info->writable = FALSE; ++ call_appsrc_writable_locked (info); ++ } ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++} ++ ++static void ++media_established_cb (PurpleMedia *media,const gchar *session_id, ++ const gchar *participant, PurpleMediaCandidate *local_candidate, ++ PurpleMediaCandidate *remote_candidate, PurpleMediaAppDataInfo *info) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ info->connected = TRUE; ++ /* We established the connection, if we were writable, then we need to ++ * signal it now */ ++ if (info->writable) ++ call_appsrc_writable_locked (info); ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++} ++ ++static GstElement * ++create_send_appsrc(PurpleMedia *media, ++ const gchar *session_id, const gchar *participant) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager, ++ media, session_id, participant); ++ GstElement *appsrc = (GstElement *)info->appsrc; ++ ++ if (appsrc == NULL) { ++ GstAppSrcCallbacks callbacks = {appsrc_need_data, appsrc_enough_data, ++ appsrc_seek_data, {NULL}}; ++ GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream"); ++ ++ appsrc = gst_element_factory_make("appsrc", NULL); ++ ++ info->appsrc = (GstAppSrc *)appsrc; ++ ++ gst_app_src_set_caps (info->appsrc, caps); ++ gst_app_src_set_callbacks (info->appsrc, ++ &callbacks, info, (GDestroyNotify) appsrc_destroyed); ++ g_signal_connect (media, "candidate-pair-established", ++ (GCallback) media_established_cb, info); ++ gst_caps_unref (caps); ++ } ++ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return appsrc; ++} ++ ++static void ++appsink_eos (GstAppSink *appsink, gpointer user_data) ++{ ++} ++ ++static GstFlowReturn ++appsink_new_preroll (GstAppSink *appsink, gpointer user_data) ++{ ++ return GST_FLOW_OK; ++} ++ ++static gboolean ++appsink_readable (gpointer user_data) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ PurpleMediaAppDataInfo *info = user_data; ++ void (*readable_cb) (PurpleMediaManager *manager, PurpleMedia *media, ++ const gchar *session_id, const gchar *participant, gpointer user_data); ++ PurpleMedia *media; ++ gchar *session_id; ++ gchar *participant; ++ gpointer cb_data; ++ guint *timer_id_ptr = &info->readable_timer_id; ++ guint timer_id = *timer_id_ptr; ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ if (timer_id == 0 || timer_id != *timer_id_ptr) { ++ /* Avoided a race condition (see writable callback) */ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return FALSE; ++ } ++ /* We need to signal readable until there are no more samples */ ++ while (info->callbacks.readable && ++ (info->num_samples > 0 || info->current_sample != NULL)) { ++ readable_cb = info->callbacks.readable; ++ media = g_weak_ref_get (&info->media_ref); ++ session_id = g_strdup (info->session_id); ++ participant = g_strdup (info->participant); ++ cb_data = info->user_data; ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ ++ if (readable_cb) ++ readable_cb (manager, media, session_id, participant, cb_data); ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ g_object_unref (media); ++ g_free (session_id); ++ g_free (participant); ++ if (timer_id == 0 || timer_id != *timer_id_ptr) { ++ /* We got cancelled */ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return FALSE; ++ } ++ } ++ info->readable_timer_id = 0; ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return FALSE; ++} ++ ++static void ++call_appsink_readable_locked (PurpleMediaAppDataInfo *info) ++{ ++ /* We must signal that a new sample has arrived to release blocking reads */ ++ g_cond_broadcast (&info->readable_cond); ++ ++ /* We already have a writable callback scheduled, don't create another one */ ++ if (info->readable_timer_id || info->callbacks.readable == NULL) ++ return; ++ ++ info->readable_timer_id = purple_timeout_add (0, appsink_readable, info); ++} ++ ++static GstFlowReturn ++appsink_new_sample (GstAppSink *appsink, gpointer user_data) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ PurpleMediaAppDataInfo *info = user_data; ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ info->num_samples++; ++ call_appsink_readable_locked (info); ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ ++ return GST_FLOW_OK; ++} ++ ++static void ++appsink_destroyed (PurpleMediaAppDataInfo *info) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ ++ g_mutex_lock (&manager->priv->appdata_mutex); ++ info->appsink = NULL; ++ info->num_samples = 0; ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++} ++ ++static GstElement * ++create_recv_appsink(PurpleMedia *media, ++ const gchar *session_id, const gchar *participant) ++{ ++ PurpleMediaManager *manager = purple_media_manager_get (); ++ PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager, ++ media, session_id, participant); ++ GstElement *appsink = (GstElement *)info->appsink; ++ ++ if (appsink == NULL) { ++ GstAppSinkCallbacks callbacks = {appsink_eos, appsink_new_preroll, ++ appsink_new_sample, {NULL}}; ++ GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream"); ++ ++ appsink = gst_element_factory_make("appsink", NULL); ++ ++ info->appsink = (GstAppSink *)appsink; ++ ++ gst_app_sink_set_caps (info->appsink, caps); ++ gst_app_sink_set_callbacks (info->appsink, ++ &callbacks, info, (GDestroyNotify) appsink_destroyed); ++ gst_caps_unref (caps); ++ ++ } ++ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return appsink; ++} ++#endif ++ ++static PurpleMediaElementInfo * ++get_send_application_element_info () ++{ ++ static PurpleMediaElementInfo *info = NULL; ++ ++#ifdef HAVE_MEDIA_APPLICATION ++ if (info == NULL) { ++ info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO, ++ "id", "pidginappsrc", ++ "name", "Pidgin Application Source", ++ "type", PURPLE_MEDIA_ELEMENT_APPLICATION ++ | PURPLE_MEDIA_ELEMENT_SRC ++ | PURPLE_MEDIA_ELEMENT_ONE_SRC, ++ "create-cb", create_send_appsrc, NULL); ++ } ++#endif ++ ++ return info; ++} ++ ++ ++static PurpleMediaElementInfo * ++get_recv_application_element_info () ++{ ++ static PurpleMediaElementInfo *info = NULL; ++ ++#ifdef HAVE_MEDIA_APPLICATION ++ if (info == NULL) { ++ info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO, ++ "id", "pidginappsink", ++ "name", "Pidgin Application Sink", ++ "type", PURPLE_MEDIA_ELEMENT_APPLICATION ++ | PURPLE_MEDIA_ELEMENT_SINK ++ | PURPLE_MEDIA_ELEMENT_ONE_SINK, ++ "create-cb", create_recv_appsink, NULL); ++ } ++#endif ++ ++ return info; ++} ++ + GstElement * + purple_media_manager_get_element(PurpleMediaManager *manager, + PurpleMediaSessionType type, PurpleMedia *media, +@@ -595,6 +1084,10 @@ purple_media_manager_get_element(PurpleM + info = manager->priv->video_src; + else if (type & PURPLE_MEDIA_RECV_VIDEO) + info = manager->priv->video_sink; ++ else if (type & PURPLE_MEDIA_SEND_APPLICATION) ++ info = get_send_application_element_info (); ++ else if (type & PURPLE_MEDIA_RECV_APPLICATION) ++ info = get_recv_application_element_info (); + + if (info == NULL) + return NULL; +@@ -834,11 +1327,16 @@ purple_media_manager_get_active_element( + return manager->priv->audio_src; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_src; ++ else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION) ++ return get_send_application_element_info (); + } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_sink; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_sink; ++ else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION) ++ return get_recv_application_element_info (); ++ + } + #endif + +@@ -1138,6 +1636,174 @@ purple_media_manager_get_backend_type(Pu + #endif + } + ++void ++purple_media_manager_set_application_data_callbacks(PurpleMediaManager *manager, ++ PurpleMedia *media, const gchar *session_id, ++ const gchar *participant, PurpleMediaAppDataCallbacks *callbacks, ++ gpointer user_data, GDestroyNotify notify) ++{ ++#ifdef HAVE_MEDIA_APPLICATION ++ PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager, ++ media, session_id, participant); ++ ++ if (info->notify) ++ info->notify (info->user_data); ++ ++ if (info->readable_timer_id) { ++ purple_timeout_remove (info->readable_timer_id); ++ info->readable_timer_id = 0; ++ } ++ ++ if (info->writable_timer_id) { ++ purple_timeout_remove (info->writable_timer_id); ++ info->writable_timer_id = 0; ++ } ++ ++ if (callbacks) { ++ info->callbacks = *callbacks; ++ } else { ++ info->callbacks.writable = NULL; ++ info->callbacks.readable = NULL; ++ } ++ info->user_data = user_data; ++ info->notify = notify; ++ ++ call_appsrc_writable_locked (info); ++ if (info->num_samples > 0 || info->current_sample != NULL) ++ call_appsink_readable_locked (info); ++ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++#endif ++} ++ ++gint ++purple_media_manager_send_application_data ( ++ PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id, ++ const gchar *participant, gpointer buffer, guint size, gboolean blocking) ++{ ++#ifdef HAVE_MEDIA_APPLICATION ++ PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager, ++ media, session_id, participant); ++ ++ if (info && info->appsrc && info->connected) { ++ GstBuffer *gstbuffer = gst_buffer_new_wrapped (g_memdup (buffer, size), ++ size); ++ GstAppSrc *appsrc = gst_object_ref (info->appsrc); ++ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ if (gst_app_src_push_buffer (appsrc, gstbuffer) == GST_FLOW_OK) { ++ if (blocking) { ++ GstPad *srcpad; ++ ++ srcpad = gst_element_get_static_pad (GST_ELEMENT (appsrc), ++ "src"); ++ if (srcpad) { ++ gst_pad_peer_query (srcpad, gst_query_new_drain ()); ++ gst_object_unref (srcpad); ++ } ++ } ++ gst_object_unref (appsrc); ++ return size; ++ } else { ++ gst_object_unref (appsrc); ++ return -1; ++ } ++ } ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return -1; ++#else ++ return -1; ++#endif ++} ++ ++gint ++purple_media_manager_receive_application_data ( ++ PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id, ++ const gchar *participant, gpointer buffer, guint max_size, ++ gboolean blocking) ++{ ++#ifdef HAVE_MEDIA_APPLICATION ++ PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager, ++ media, session_id, participant); ++ guint bytes_read = 0; ++ ++ if (info) { ++ /* If we are in a blocking read, we need to loop until max_size data ++ * is read into the buffer, if we're not, then we need to read as much ++ * data as possible ++ */ ++ do { ++ if (!info->current_sample && info->appsink && info->num_samples > 0) { ++ info->current_sample = gst_app_sink_pull_sample (info->appsink); ++ info->sample_offset = 0; ++ if (info->current_sample) ++ info->num_samples--; ++ } ++ ++ if (info->current_sample) { ++ GstBuffer *gstbuffer = gst_sample_get_buffer ( ++ info->current_sample); ++ ++ if (gstbuffer) { ++ GstMapInfo mapinfo; ++ guint bytes_to_copy; ++ ++ gst_buffer_map (gstbuffer, &mapinfo, GST_MAP_READ); ++ /* We must copy only the data remaining in the buffer without ++ * overflowing the buffer */ ++ bytes_to_copy = max_size - bytes_read; ++ if (bytes_to_copy > mapinfo.size - info->sample_offset) ++ bytes_to_copy = mapinfo.size - info->sample_offset; ++ memcpy ((guint8 *)buffer + bytes_read, ++ mapinfo.data + info->sample_offset, bytes_to_copy); ++ ++ gst_buffer_unmap (gstbuffer, &mapinfo); ++ info->sample_offset += bytes_to_copy; ++ bytes_read += bytes_to_copy; ++ if (info->sample_offset == mapinfo.size) { ++ gst_sample_unref (info->current_sample); ++ info->current_sample = NULL; ++ info->sample_offset = 0; ++ } ++ } else { ++ /* In case there's no buffer in the sample (should never ++ * happen), we need to at least unref it */ ++ gst_sample_unref (info->current_sample); ++ info->current_sample = NULL; ++ info->sample_offset = 0; ++ } ++ } ++ ++ /* If blocking, wait until there's an available sample */ ++ while (bytes_read < max_size && blocking && ++ info->current_sample == NULL && info->num_samples == 0) { ++ g_cond_wait (&info->readable_cond, &manager->priv->appdata_mutex); ++ ++ /* We've been signaled, we need to unlock and regrab the info ++ * struct to make sure nothing changed */ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ info = get_app_data_info_and_lock (manager, ++ media, session_id, participant); ++ if (info == NULL || info->appsink == NULL) { ++ /* The session was destroyed while we were waiting, we ++ * should return here */ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return bytes_read; ++ } ++ } ++ } while (bytes_read < max_size && ++ (blocking || info->num_samples > 0)); ++ ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return bytes_read; ++ } ++ g_mutex_unlock (&manager->priv->appdata_mutex); ++ return -1; ++#else ++ return -1; ++#endif ++} ++ + #ifdef USE_GSTREAMER + + /* +@@ -1185,6 +1851,8 @@ purple_media_element_type_get_type() + "PURPLE_MEDIA_ELEMENT_SRC", "src" }, + { PURPLE_MEDIA_ELEMENT_SINK, + "PURPLE_MEDIA_ELEMENT_SINK", "sink" }, ++ { PURPLE_MEDIA_ELEMENT_APPLICATION, ++ "PURPLE_MEDIA_ELEMENT_APPLICATION", "application" }, + { 0, NULL, NULL } + }; + type = g_flags_register_static( +@@ -1410,5 +2078,6 @@ purple_media_element_info_call_create(Pu + return NULL; + } + ++ + #endif /* USE_GSTREAMER */ + +Index: pidgin-2.10.11/libpurple/mediamanager.h +=================================================================== +--- pidgin-2.10.11.orig/libpurple/mediamanager.h ++++ pidgin-2.10.11/libpurple/mediamanager.h +@@ -38,6 +38,30 @@ typedef struct _PurpleMediaManagerClass + #include "account.h" + #include "media.h" + ++/** ++ * PurpleMediaAppDataCallbacks: ++ * @readable: Called when the stream has received data and is readable. ++ * @writable: Called when the stream has become writable or has stopped being ++ * writable. ++ * ++ * A set of callbacks that can be installed on an Application data session with ++ * purple_media_manager_set_application_data_callbacks() ++ * ++ * Once installed the @readable callback will get called as long as data is ++ * available to read, so the data must be read completely. ++ * The @writable callback will only be called when the writable state of the ++ * stream changes. The @writable argument defines whether the stream has ++ * become writable or stopped being writable. ++ * ++ */ ++typedef struct { ++ void (*readable) (PurpleMediaManager *manager, PurpleMedia *media, ++ const gchar *session_id, const gchar *participant, gpointer user_data); ++ void (*writable) (PurpleMediaManager *manager, PurpleMedia *media, ++ const gchar *session_id, const gchar *participant, gboolean writable, ++ gpointer user_data); ++} PurpleMediaAppDataCallbacks; ++ + G_BEGIN_DECLS + + #define PURPLE_TYPE_MEDIA_MANAGER (purple_media_manager_get_type()) +@@ -285,6 +309,71 @@ void purple_media_manager_set_backend_ty + */ + GType purple_media_manager_get_backend_type(PurpleMediaManager *manager); + ++/** ++ * purple_media_manager_set_application_data_callbacks: ++ * @manager: The manager to register the callbacks with. ++ * @media: The media instance to register the callbacks with. ++ * @session_id: The session to register the callbacks with. ++ * @participant: The participant to register the callbacks with. ++ * @callbacks: The callbacks to be set on the session. ++ * @user_data: a user_data argument for the callbacks. ++ * @notify: a destroy notify function. ++ * ++ * Set callbacks on a session to be called when the stream becomes writable ++ * or readable for media sessions of type #PURPLE_MEDIA_APPLICATION ++ */ ++void purple_media_manager_set_application_data_callbacks( ++ PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id, ++ const gchar *participant, PurpleMediaAppDataCallbacks *callbacks, ++ gpointer user_data, GDestroyNotify notify); ++ ++/** ++ * purple_media_manager_send_application_data: ++ * @manager: The manager to send data with. ++ * @media: The media instance to which the session belongs. ++ * @session_id: The session to send data to. ++ * @participant: The participant to send data to. ++ * @buffer: The buffer of data to send. ++ * @size: The size of @buffer ++ * @blocking: Whether to block until the data was send or not. ++ * ++ * Sends a buffer of data to a #PURPLE_MEDIA_APPLICATION session. ++ * If @blocking is set, unless an error occured, the function will not return ++ * until the data has been flushed into the network. ++ * If the stream is not writable, the data will be queued. It is the ++ * responsability of the user to stop sending data when the stream isn't ++ * writable anymore. It is also the responsability of the user to only start ++ * sending data after the stream has been configured correctly (encryption ++ * parameters for example). ++ * ++ * Returns: Number of bytes sent or -1 in case of error. ++ */ ++gint purple_media_manager_send_application_data ( ++ PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id, ++ const gchar *participant, gpointer buffer, guint size, gboolean blocking); ++ ++/** ++ * purple_media_manager_receive_application_data: ++ * @manager: The manager to receive data with. ++ * @media: The media instance to which the session belongs. ++ * @session_id: The session to receive data from. ++ * @participant: The participant to receive data from. ++ * @buffer: The buffer to receive data into. ++ * @max_size: The max_size of @buffer ++ * @blocking: Whether to block until the buffer is entirely filled or return ++ * with currently available data. ++ * ++ * Receive a buffer of data from a #PURPLE_MEDIA_APPLICATION session. ++ * If @blocking is set, unless an error occured, the function will not return ++ * until @max_size bytes are read. ++ * ++ * Returns: Number of bytes received or -1 in case of error. ++ */ ++gint purple_media_manager_receive_application_data ( ++ PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id, ++ const gchar *participant, gpointer buffer, guint max_size, ++ gboolean blocking); ++ + /*}@*/ + + #ifdef __cplusplus diff --git a/pidgin-2.10.11-gst-references.patch b/pidgin-2.10.11-gst-references.patch new file mode 100644 index 0000000..94df8cd --- /dev/null +++ b/pidgin-2.10.11-gst-references.patch @@ -0,0 +1,44 @@ + +# HG changeset patch +# User Youness Alaoui +# Date 1404874798 14400 +# Node ID b52be4fef1ded825e262095480915f2c675a5694 +# Parent 2b41ba1fde8a80a4f2d715874a0b02449d4b66ad +Fix gstreamer elements references +In backend-fs2, the create_src will unref the src after it's done with +it, if we simply return the created source, it will segfault. +The issue never happened before because every source so far also has +the UNIQUE flag, which causes it to go in a different branch of the +code which does ref the element and add it to the bin. + +Refs #16315 + +diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c +--- a/libpurple/mediamanager.c ++++ b/libpurple/mediamanager.c +@@ -443,7 +443,11 @@ + + if (src) { + GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "prpl_video_caps"); +- g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL); ++ if (capsfilter) { ++ g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL); ++ gst_object_unref (capsfilter); ++ } ++ gst_object_unref (src); + } + + g_free(id); +@@ -550,6 +554,11 @@ + } else { + ret = purple_media_element_info_call_create(info, + media, session_id, participant); ++ if (element_type & PURPLE_MEDIA_ELEMENT_SRC) { ++ gst_object_ref(ret); ++ gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(manager)), ++ ret); ++ } + } + + if (ret == NULL) + diff --git a/pidgin-2.10.11-init-media-optional.patch b/pidgin-2.10.11-init-media-optional.patch new file mode 100644 index 0000000..9f62ad8 --- /dev/null +++ b/pidgin-2.10.11-init-media-optional.patch @@ -0,0 +1,52 @@ + +# HG changeset patch +# User Jakub Adam +# Date 1407847148 -7200 +# Node ID 7767aaeade6404396204794f9bc75d9a2cb723f0 +# Parent 8e4fa54f166211ffd6cd869cca611e8d64ea1fdf +media: make "init-media" signal handler optional + +Change the logic so that the PurpleMedia instance isn't disposed when +"init-media" has no connected handlers. + +We want the media object freed only when some signal callback function +explicitly returns FALSE, indicating an error during the initialization. + +This is mostly useful for the imminent addition of private media streams. + +diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c +--- a/libpurple/mediamanager.c ++++ b/libpurple/mediamanager.c +@@ -334,7 +334,7 @@ + { + #ifdef USE_VV + PurpleMedia *media; +- gboolean signal_ret; ++ guint signal_id; + + media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), + "manager", manager, +@@ -343,12 +343,17 @@ + "initiator", initiator, + NULL)); + +- g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0, +- media, account, remote_user, &signal_ret); ++ signal_id = purple_media_manager_signals[INIT_MEDIA]; + +- if (signal_ret == FALSE) { +- g_object_unref(media); +- return NULL; ++ if (g_signal_has_handler_pending(manager, signal_id, 0, FALSE)) { ++ gboolean signal_ret; ++ ++ g_signal_emit(manager, signal_id, 0, media, account, remote_user, ++ &signal_ret); ++ if (signal_ret == FALSE) { ++ g_object_unref(media); ++ return NULL; ++ } + } + + manager->priv->medias = g_list_append(manager->priv->medias, media); + diff --git a/pidgin-2.10.11-private-media.patch b/pidgin-2.10.11-private-media.patch new file mode 100644 index 0000000..8b9e961 --- /dev/null +++ b/pidgin-2.10.11-private-media.patch @@ -0,0 +1,290 @@ + +# HG changeset patch +# User Youness Alaoui +# Date 1404764862 14400 +# Node ID d729a9b2126594df3e38647e926ac7c0a7db807b +# Parent 7767aaeade6404396204794f9bc75d9a2cb723f0 +Add Private media API + +Creating a private media can be useful for plugins that want to create a +PurpleMedia for internal use without the front-end being notified of its +creation. + +diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c +--- a/libpurple/mediamanager.c ++++ b/libpurple/mediamanager.c +@@ -86,6 +86,7 @@ + GstElement *pipeline; + PurpleMediaCaps ui_caps; + GList *medias; ++ GList *private_medias; + GList *elements; + GList *output_windows; + gulong next_output_window_id; +@@ -111,6 +112,7 @@ + + enum { + INIT_MEDIA, ++ INIT_PRIVATE_MEDIA, + UI_CAPS_CHANGED, + LAST_SIGNAL + }; +@@ -161,6 +163,15 @@ + G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA, + G_TYPE_POINTER, G_TYPE_STRING); + ++ purple_media_manager_signals[INIT_PRIVATE_MEDIA] = ++ g_signal_new ("init-private-media", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, NULL, NULL, ++ purple_smarshal_BOOLEAN__OBJECT_POINTER_STRING, ++ G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA, ++ G_TYPE_POINTER, G_TYPE_STRING); ++ + purple_media_manager_signals[UI_CAPS_CHANGED] = g_signal_new ("ui-caps-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, +@@ -177,6 +188,7 @@ + { + media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media); + media->priv->medias = NULL; ++ media->priv->private_medias = NULL; + media->priv->next_output_window_id = 1; + #ifdef USE_VV + media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2; +@@ -198,6 +210,10 @@ + g_list_delete_link(priv->medias, priv->medias)) { + g_object_unref(priv->medias->data); + } ++ for (; priv->private_medias; priv->private_medias = ++ g_list_delete_link(priv->private_medias, priv->private_medias)) { ++ g_object_unref(priv->private_medias->data); ++ } + for (; priv->elements; priv->elements = + g_list_delete_link(priv->elements, priv->elements)) { + g_object_unref(priv->elements->data); +@@ -325,12 +341,13 @@ + } + #endif /* USE_GSTREAMER */ + +-PurpleMedia * +-purple_media_manager_create_media(PurpleMediaManager *manager, +- PurpleAccount *account, +- const char *conference_type, +- const char *remote_user, +- gboolean initiator) ++static PurpleMedia * ++create_media(PurpleMediaManager *manager, ++ PurpleAccount *account, ++ const char *conference_type, ++ const char *remote_user, ++ gboolean initiator, ++ gboolean private) + { + #ifdef USE_VV + PurpleMedia *media; +@@ -343,7 +360,9 @@ + "initiator", initiator, + NULL)); + +- signal_id = purple_media_manager_signals[INIT_MEDIA]; ++ signal_id = private ? ++ purple_media_manager_signals[INIT_PRIVATE_MEDIA] : ++ purple_media_manager_signals[INIT_MEDIA]; + + if (g_signal_has_handler_pending(manager, signal_id, 0, FALSE)) { + gboolean signal_ret; +@@ -356,26 +375,33 @@ + } + } + +- manager->priv->medias = g_list_append(manager->priv->medias, media); ++ if (private) ++ manager->priv->private_medias = g_list_append( ++ manager->priv->private_medias, media); ++ else ++ manager->priv->medias = g_list_append(manager->priv->medias, media); + return media; + #else + return NULL; + #endif + } + +-GList * +-purple_media_manager_get_media(PurpleMediaManager *manager) ++static GList * ++get_media(PurpleMediaManager *manager, gboolean private) + { + #ifdef USE_VV +- return manager->priv->medias; ++ if (private) ++ return manager->priv->private_medias; ++ else ++ return manager->priv->medias; + #else + return NULL; + #endif + } + +-GList * +-purple_media_manager_get_media_by_account(PurpleMediaManager *manager, +- PurpleAccount *account) ++static GList * ++get_media_by_account(PurpleMediaManager *manager, ++ PurpleAccount *account, gboolean private) + { + #ifdef USE_VV + GList *media = NULL; +@@ -383,7 +409,10 @@ + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + +- iter = manager->priv->medias; ++ if (private) ++ iter = manager->priv->private_medias; ++ else ++ iter = manager->priv->medias; + for (; iter; iter = g_list_next(iter)) { + if (purple_media_get_account(iter->data) == account) { + media = g_list_prepend(media, iter->data); +@@ -397,21 +426,73 @@ + } + + void +-purple_media_manager_remove_media(PurpleMediaManager *manager, +- PurpleMedia *media) ++purple_media_manager_remove_media(PurpleMediaManager *manager, PurpleMedia *media) + { + #ifdef USE_VV + GList *list; ++ GList **medias; + + g_return_if_fail(manager != NULL); + +- list = g_list_find(manager->priv->medias, media); ++ if ((list = g_list_find(manager->priv->medias, media))) { ++ medias = &manager->priv->medias; ++ } else if ((list = g_list_find(manager->priv->private_medias, media))) { ++ medias = &manager->priv->private_medias; ++ } ++ + if (list) +- manager->priv->medias = +- g_list_delete_link(manager->priv->medias, list); ++ *medias = g_list_delete_link(*medias, list); + #endif + } + ++PurpleMedia * ++purple_media_manager_create_media(PurpleMediaManager *manager, ++ PurpleAccount *account, ++ const char *conference_type, ++ const char *remote_user, ++ gboolean initiator) ++{ ++ return create_media (manager, account, conference_type, ++ remote_user, initiator, FALSE); ++} ++ ++GList * ++purple_media_manager_get_media(PurpleMediaManager *manager) ++{ ++ return get_media (manager, FALSE); ++} ++ ++GList * ++purple_media_manager_get_media_by_account(PurpleMediaManager *manager, ++ PurpleAccount *account) ++{ ++ return get_media_by_account (manager, account, FALSE); ++} ++ ++PurpleMedia * ++purple_media_manager_create_private_media(PurpleMediaManager *manager, ++ PurpleAccount *account, ++ const char *conference_type, ++ const char *remote_user, ++ gboolean initiator) ++{ ++ return create_media (manager, account, conference_type, ++ remote_user, initiator, TRUE); ++} ++ ++GList * ++purple_media_manager_get_private_media(PurpleMediaManager *manager) ++{ ++ return get_media (manager, TRUE); ++} ++ ++GList * ++purple_media_manager_get_private_media_by_account(PurpleMediaManager *manager, ++ PurpleAccount *account) ++{ ++ return get_media_by_account (manager, account, TRUE); ++} ++ + #ifdef USE_VV + static void + request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data) +diff --git a/libpurple/mediamanager.h b/libpurple/mediamanager.h +--- a/libpurple/mediamanager.h ++++ b/libpurple/mediamanager.h +@@ -130,6 +130,56 @@ + PurpleMedia *media); + + /** ++ * Creates a private media session. A private media session is a ++ * media session which is private to the caller. It is meant to be ++ * used by plugins to create a media session that the front-end does ++ * not get notified about. It is useful especially for sessions with a ++ * type of PURPLE_MEDIA_APPLICATION which the front-end wouldn't know ++ * how to handle. ++ * ++ * @param manager The media manager to create the session under. ++ * @param account The account to create the session on. ++ * @param conference_type The conference type to feed into Farsight2. ++ * @param remote_user The remote user to initiate the session with. ++ * @param initiator TRUE if the local user is the initiator of this media ++ * call, FALSE otherwise. ++ * ++ * @return A newly created media session. ++ * ++ * @since 2.11.0 ++ */ ++PurpleMedia *purple_media_manager_create_private_media( ++ PurpleMediaManager *manager, ++ PurpleAccount *account, ++ const char *conference_type, ++ const char *remote_user, ++ gboolean initiator); ++ ++/** ++ * Gets all of the private media sessions. ++ * ++ * @param manager The media manager to get all of the sessions from. ++ * ++ * @return A list of all the private media sessions. ++ * ++ * @since 2.11.0 ++ */ ++GList *purple_media_manager_get_private_media(PurpleMediaManager *manager); ++ ++/** ++ * Gets all of the private media sessions for a given account. ++ * ++ * @param manager The media manager to get the sessions from. ++ * @param account The account the sessions are on. ++ * ++ * @return A list of the private media sessions on the given account. ++ * ++ * @since 2.11.0 ++ */ ++GList *purple_media_manager_get_private_media_by_account( ++ PurpleMediaManager *manager, PurpleAccount *account); ++ ++/** + * Signals that output windows should be created for the chosen stream. + * + * This shouldn't be called outside of mediamanager.c and media.c + diff --git a/pidgin-2.10.11-send-video-enum.patch b/pidgin-2.10.11-send-video-enum.patch new file mode 100644 index 0000000..41803ec --- /dev/null +++ b/pidgin-2.10.11-send-video-enum.patch @@ -0,0 +1,23 @@ + +# HG changeset patch +# User Youness Alaoui +# Date 1404159462 14400 +# Node ID 2b41ba1fde8a80a4f2d715874a0b02449d4b66ad +# Parent f02f7d1fb4d545c6d7353745094e1afcf0428ca9 +Fix send-video enum typo + +Refs #16315 + +diff --git a/libpurple/media/enum-types.c b/libpurple/media/enum-types.c +--- a/libpurple/media/enum-types.c ++++ b/libpurple/media/enum-types.c +@@ -175,7 +175,7 @@ + { PURPLE_MEDIA_RECV_VIDEO, + "PURPLE_MEDIA_RECV_VIDEO", "recv-video" }, + { PURPLE_MEDIA_SEND_VIDEO, +- "PURPLE_MEDIA_SEND_VIDEO", "send-audio" }, ++ "PURPLE_MEDIA_SEND_VIDEO", "send-video" }, + { PURPLE_MEDIA_AUDIO, + "PURPLE_MEDIA_AUDIO", "audio" }, + { PURPLE_MEDIA_VIDEO, + diff --git a/pidgin-port-to-gst-1.0.patch b/pidgin-port-to-gst-1.0.patch new file mode 100644 index 0000000..75beec3 --- /dev/null +++ b/pidgin-port-to-gst-1.0.patch @@ -0,0 +1,1128 @@ + +# HG changeset patch +# User David Woodhouse +# Date 1426073959 0 +# Node ID 2415067473ba10a2090d6130b93204a3b537b05c +# Parent 6b4576edf2a694ab55d0d06d3643c44601a75b15 +Support GStreamer 1.x and resync with trunk + +Index: pidgin-2.10.11/configure.ac +=================================================================== +--- pidgin-2.10.11.orig/configure.ac ++++ pidgin-2.10.11/configure.ac +@@ -741,42 +741,115 @@ AM_GCONF_SOURCE_2 + dnl ####################################################################### + dnl # Check for GStreamer + dnl ####################################################################### +-dnl +-dnl TODO: Depend on gstreamer >= 0.10.10, and remove the conditional use of +-dnl gst_registry_fork_set_enabled. + AC_ARG_ENABLE(gstreamer, +- [AC_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])], ++ [AS_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])], + enable_gst="$enableval", enable_gst="yes") ++AC_ARG_WITH(gstreamer, [AS_HELP_STRING([--with-gstreamer=], ++ [compile with GStreamer 0.10 or 1.0 interface (default: auto)])], ++ with_gstreamer="$withval", with_gstreamer="auto") + if test "x$enable_gst" != "xno"; then +- PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [ +- AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) +- AC_SUBST(GSTREAMER_CFLAGS) +- AC_SUBST(GSTREAMER_LIBS) +- AC_CHECK_LIB(gstreamer-0.10, gst_registry_fork_set_enabled, +- [ AC_DEFINE(GST_CAN_DISABLE_FORKING, [], +- [Define if gst_registry_fork_set_enabled exists])], +- [], [$GSTREAMER_LIBS]) +- ], [ +- AC_MSG_RESULT(no) +- enable_gst="no" +- if test "x$force_deps" = "xyes" ; then +- AC_MSG_ERROR([ ++ if test "x$with_gstreamer" == "xauto"; then ++ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [ ++ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) ++ with_gstreamer="1.0" ++ AC_SUBST(GSTREAMER_CFLAGS) ++ AC_SUBST(GSTREAMER_LIBS) ++ dnl Check whether forking stuff is required for this version. ++ ], [ ++ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [ ++ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) ++ with_gstreamer="0.10" ++ AC_SUBST(GSTREAMER_CFLAGS) ++ AC_SUBST(GSTREAMER_LIBS) ++ ], [ ++ AC_MSG_RESULT(no) ++ enable_gst="no" ++ if test "x$force_deps" = "xyes" ; then ++ AC_MSG_ERROR([ + GStreamer development headers not found. + Use --disable-gstreamer if you do not need GStreamer (sound) support. + ]) +- fi]) ++ fi ++ ]) ++ ]) ++ elif test "x$with_gstreamer" == "x1.0"; then ++ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [ ++ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 1.0 for playing sounds]) ++ AC_SUBST(GSTREAMER_CFLAGS) ++ AC_SUBST(GSTREAMER_LIBS) ++ ], [ ++ AC_MSG_RESULT(no) ++ enable_gst="no" ++ if test "x$force_deps" = "xyes" ; then ++ AC_MSG_ERROR([ ++GStreamer development headers not found. ++Use --disable-gstreamer if you do not need GStreamer (sound) support. ++]) ++ fi ++ ]) ++ elif test "x$with_gstreamer" == "x0.10"; then ++ PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [ ++ AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 0.10 for playing sounds]) ++ AC_SUBST(GSTREAMER_CFLAGS) ++ AC_SUBST(GSTREAMER_LIBS) ++ ], [ ++ AC_MSG_RESULT(no) ++ enable_gst="no" ++ if test "x$force_deps" = "xyes" ; then ++ AC_MSG_ERROR([ ++GStreamer development headers not found. ++Use --disable-gstreamer if you do not need GStreamer (sound) support. ++]) ++ fi ++ ]) ++ else ++ AC_MSG_ERROR([--with-gstreamer must specify one of 0.10, 1.0 or auto.]) ++ fi ++fi ++ ++if test "x$with_gtk" == "x3" -a "x$with_gstreamer" == "x0.10"; then ++ AC_MSG_ERROR([WebKitGTK+ 3.0 cannot be mixed with GStreamer 0.10. ++Please switch to WebKitGTK+ 2.0 or GStreamer 1.0.]) ++elif test "x$with_gtk" == "x2" -a "x$with_gstreamer" == "x1.0"; then ++ AC_MSG_ERROR([WebKitGTK+ 2.0 cannot be mixed with GStreamer 1.0. ++Please switch to WebKitGTK+ 3.0 or GStreamer 0.10.]) ++fi ++if test "x$with_gstreamer" == "x0.10" -o "x$with_gstreamer" == "x1.0"; then ++ AC_SUBST(GSTREAMER_VER, [$with_gstreamer]) ++else ++ AC_SUBST(GSTREAMER_VER, "") ++fi ++ ++dnl ####################################################################### ++dnl # Check for GStreamer Video ++dnl ####################################################################### ++if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0"; then ++ AC_ARG_ENABLE(gstreamer-video, ++ [AS_HELP_STRING([--disable-gstreamer-video], [compile without GStreamer 1.0 Video Overlay support])], ++ enable_gstvideo="$enableval", enable_gstvideo="yes") ++ if test "x$enable_gstvideo" != "xno"; then ++ PKG_CHECK_MODULES(GSTVIDEO, [gstreamer-video-1.0], [ ++ AC_DEFINE(USE_GSTVIDEO, 1, [Use GStreamer Video Overlay support]) ++ AC_SUBST(GSTVIDEO_CFLAGS) ++ AC_SUBST(GSTVIDEO_LIBS) ++ ], [ ++ enable_gstvideo="no" ++ ]) ++ fi ++else ++ enable_gstvideo="no" + fi + + dnl ####################################################################### + dnl # Check for GStreamer Interfaces + dnl ####################################################################### +-if test "x$enable_gst" != "xno"; then ++if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10"; then + AC_ARG_ENABLE(gstreamer-interfaces, +- [AC_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer interface support])], ++ [AS_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer 0.10 interface support])], + enable_gstinterfaces="$enableval", enable_gstinterfaces="yes") + if test "x$enable_gstinterfaces" != "xno"; then + PKG_CHECK_MODULES(GSTINTERFACES, [gstreamer-interfaces-0.10], [ +- AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer interfaces for X overlay support]) ++ AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer 0.10 interfaces for X overlay support]) + AC_SUBST(GSTINTERFACES_CFLAGS) + AC_SUBST(GSTINTERFACES_LIBS) + ], [ +@@ -791,32 +864,43 @@ dnl #################################### + dnl # Check for Farstream + dnl ####################################################################### + AC_ARG_ENABLE(farstream, +- [AC_HELP_STRING([--disable-farstream], [compile without farstream support])], ++ [AS_HELP_STRING([--disable-farstream], [compile without farstream support])], + enable_farstream="$enableval", enable_farstream="yes") + if test "x$enable_farstream" != "xno"; then +- PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [ +- AC_SUBST(FARSTREAM_CFLAGS) +- AC_SUBST(FARSTREAM_LIBS) +- ], [ +- # Try farsight. +- PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [ +- AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream]) ++ if test "x$with_gstreamer" == "x1.0"; then ++ PKG_CHECK_MODULES(FARSTREAM, [farstream-0.2], [ + AC_SUBST(FARSTREAM_CFLAGS) + AC_SUBST(FARSTREAM_LIBS) + ], [ + enable_farstream="no" + ]) +- ]) +- fi ++ else ++ PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [ ++ AC_SUBST(FARSTREAM_CFLAGS) ++ AC_SUBST(FARSTREAM_LIBS) ++ ], [ ++ # Try farsight. ++ PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [ ++ AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream]) ++ AC_SUBST(FARSTREAM_CFLAGS) ++ AC_SUBST(FARSTREAM_LIBS) ++ ], [ ++ enable_farstream="no" ++ ]) ++ ]) ++ fi ++fi + + dnl ####################################################################### + dnl # Check for Voice and Video support + dnl ####################################################################### + AC_ARG_ENABLE(vv, +- [AC_HELP_STRING([--disable-vv], [compile without voice and video support])], ++ [AS_HELP_STRING([--disable-vv], [compile without voice and video support])], + enable_vv="$enableval", enable_vv="yes") + if test "x$enable_vv" != "xno"; then +- if test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then ++ if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0" -a "x$enable_gstvideo" != "xno" -a "x$enable_farstream" != "xno"; then ++ AC_DEFINE(USE_VV, 1, [Use voice and video]) ++ elif test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then + AC_DEFINE(USE_VV, 1, [Use voice and video]) + else + enable_vv="no" +@@ -829,7 +913,7 @@ Or use --disable-vv if you do not need v + fi + fi + fi +-AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno") ++AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno") + + dnl ####################################################################### + dnl # Check for Internationalized Domain Name support +@@ -2619,6 +2703,7 @@ echo Protocols to build dynamically : $D + echo Protocols to link statically.. : $STATIC_PRPLS + echo + echo Build with GStreamer support.. : $enable_gst ++echo Build for GStreamer version... : $with_gstreamer + echo Build with D-Bus support...... : $enable_dbus + echo Build with voice and video.... : $enable_vv + if test "x$enable_dbus" = "xyes" ; then +Index: pidgin-2.10.11/finch/Makefile.am +=================================================================== +--- pidgin-2.10.11.orig/finch/Makefile.am ++++ pidgin-2.10.11/finch/Makefile.am +@@ -73,6 +73,7 @@ finch_LDADD = \ + $(LIBXML_LIBS) \ + $(GNT_LIBS) \ + $(GSTREAMER_LIBS) \ ++ $(GSTVIDEO_LIBS) \ + ./libgnt/libgnt.la \ + $(top_builddir)/libpurple/libpurple.la + +Index: pidgin-2.10.11/libpurple/Makefile.am +=================================================================== +--- pidgin-2.10.11.orig/libpurple/Makefile.am ++++ pidgin-2.10.11/libpurple/Makefile.am +@@ -313,6 +313,7 @@ libpurple_la_LIBADD = \ + $(INTLLIBS) \ + $(FARSTREAM_LIBS) \ + $(GSTREAMER_LIBS) \ ++ $(GSTVIDEO_LIBS) \ + $(GSTINTERFACES_LIBS) \ + $(IDN_LIBS) \ + ciphers/libpurple-ciphers.la \ +@@ -329,6 +330,7 @@ AM_CPPFLAGS = \ + $(LIBXML_CFLAGS) \ + $(FARSTREAM_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ ++ $(GSTVIDEO_CFLAGS) \ + $(GSTINTERFACES_CFLAGS) \ + $(IDN_CFLAGS) \ + $(NETWORKMANAGER_CFLAGS) \ +Index: pidgin-2.10.11/libpurple/data/purple.pc.in +=================================================================== +--- pidgin-2.10.11.orig/libpurple/data/purple.pc.in ++++ pidgin-2.10.11/libpurple/data/purple.pc.in +@@ -5,6 +5,7 @@ includedir=@includedir@ + datarootdir=@datarootdir@ + datadir=@datadir@ + sysconfdir=@sysconfdir@ ++gstreamer=@GSTREAMER_VER@ + + plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@ + +Index: pidgin-2.10.11/libpurple/example/Makefile.am +=================================================================== +--- pidgin-2.10.11.orig/libpurple/example/Makefile.am ++++ pidgin-2.10.11/libpurple/example/Makefile.am +@@ -8,6 +8,7 @@ nullclient_LDADD = \ + $(INTLLIBS) \ + $(GLIB_LIBS) \ + $(LIBXML_LIBS) \ ++ $(GSTVIDEO_LIBS) \ + $(top_builddir)/libpurple/libpurple.la + + AM_CPPFLAGS = \ +Index: pidgin-2.10.11/libpurple/media/backend-fs2.c +=================================================================== +--- pidgin-2.10.11.orig/libpurple/media/backend-fs2.c ++++ pidgin-2.10.11/libpurple/media/backend-fs2.c +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #endif + + /** @copydoc _PurpleMediaBackendFs2Class */ +@@ -242,9 +243,17 @@ purple_media_network_protocol_from_fs(Fs + g_return_val_if_reached(PURPLE_MEDIA_NETWORK_PROTOCOL_TCP); + } + ++#if GST_CHECK_VERSION(1,0,0) ++static GstPadProbeReturn ++event_probe_cb(GstPad *srcpad, GstPadProbeInfo *info, gpointer unused) ++#else + static gboolean + event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad) ++#endif + { ++#if GST_CHECK_VERSION(1,0,0) ++ GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info); ++#endif + if (GST_EVENT_TYPE(event) == GST_EVENT_CUSTOM_DOWNSTREAM + && gst_event_has_name(event, "purple-unlink-tee")) { + +@@ -252,22 +261,40 @@ event_probe_cb(GstPad *srcpad, GstEvent + + gst_pad_unlink(srcpad, gst_pad_get_peer(srcpad)); + ++#if GST_CHECK_VERSION(1,0,0) ++ gst_pad_remove_probe(srcpad, ++ g_value_get_ulong(gst_structure_get_value(s, "handler-id"))); ++#else + gst_pad_remove_event_probe(srcpad, + g_value_get_uint(gst_structure_get_value(s, "handler-id"))); ++#endif + + if (g_value_get_boolean(gst_structure_get_value(s, "release-pad"))) + gst_element_release_request_pad(GST_ELEMENT_PARENT(srcpad), srcpad); + ++#if GST_CHECK_VERSION(1,0,0) ++ return GST_PAD_PROBE_DROP; ++#else + return FALSE; ++#endif + } + ++#if GST_CHECK_VERSION(1,0,0) ++ return GST_PAD_PROBE_OK; ++#else + return TRUE; ++#endif + } + + static void + unlink_teepad_dynamic(GstPad *srcpad, gboolean release_pad) + { ++#if GST_CHECK_VERSION(1,0,0) ++ gulong id = gst_pad_add_probe(srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, ++ event_probe_cb, NULL, NULL); ++#else + guint id = gst_pad_add_event_probe(srcpad, G_CALLBACK(event_probe_cb), NULL); ++#endif + + if (GST_IS_GHOST_PAD(srcpad)) + srcpad = gst_ghost_pad_get_target(GST_GHOST_PAD(srcpad)); +@@ -276,7 +303,11 @@ unlink_teepad_dynamic(GstPad *srcpad, gb + gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM, + gst_structure_new("purple-unlink-tee", + "release-pad", G_TYPE_BOOLEAN, release_pad, ++#if GST_CHECK_VERSION(1,0,0) ++ "handler-id", G_TYPE_ULONG, id, ++#else + "handler-id", G_TYPE_UINT, id, ++#endif + NULL))); + } + +@@ -859,15 +890,31 @@ gst_msg_db_to_percent(GstMessage *msg, g + gdouble value_db; + gdouble percent; + +- list = gst_structure_get_value( +- gst_message_get_structure(msg), value_name); ++ list = gst_structure_get_value(gst_message_get_structure(msg), value_name); ++#if GST_CHECK_VERSION(1,0,0) ++G_GNUC_BEGIN_IGNORE_DEPRECATIONS ++ value = g_value_array_get_nth(g_value_get_boxed(list), 0); ++G_GNUC_END_IGNORE_DEPRECATIONS ++#else + value = gst_value_list_get_value(list, 0); ++#endif + value_db = g_value_get_double(value); + percent = pow(10, value_db / 20); + return (percent > 1.0) ? 1.0 : percent; + } + + static void ++purple_media_error_fs(PurpleMedia *media, const gchar *error, ++ const GstStructure *fs_error) ++{ ++ const gchar *error_msg = gst_structure_get_string(fs_error, "error-msg"); ++ ++ purple_media_error(media, "%s%s%s", error, ++ error_msg ? _("\n\nMessage from Farsight: ") : "", ++ error_msg ? error_msg : ""); ++} ++ ++static void + gst_handle_message_element(GstBus *bus, GstMessage *msg, + PurpleMediaBackendFs2 *self) + { +@@ -875,11 +922,12 @@ gst_handle_message_element(GstBus *bus, + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + static guint level_id = 0; ++ const GstStructure *structure = gst_message_get_structure(msg); + + if (level_id == 0) + level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA); + +- if (gst_structure_has_name(msg->structure, "level")) { ++ if (gst_structure_has_name(structure, "level")) { + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + gchar *name; + gchar *participant = NULL; +@@ -934,31 +982,63 @@ gst_handle_message_element(GstBus *bus, + return; + + #ifdef HAVE_FARSIGHT +- if (gst_structure_has_name(msg->structure, "farsight-error")) { ++ if (gst_structure_has_name(structure, "farsight-error")) { + #else +- if (gst_structure_has_name(msg->structure, "farstream-error")) { ++ if (gst_structure_has_name(structure, "farstream-error")) { + #endif + FsError error_no; +- gst_structure_get_enum(msg->structure, "error-no", ++ gboolean error_emitted = FALSE; ++ gst_structure_get_enum(structure, "error-no", + FS_TYPE_ERROR, (gint*)&error_no); + switch (error_no) { ++ case FS_ERROR_CONSTRUCTION: ++ purple_media_error_fs(priv->media, ++ _("Error initializing the call. " ++ "This probably denotes problem in " ++#ifdef HAVE_FARSIGHT ++ "installation of GStreamer or Farsight."), ++#else ++ "installation of GStreamer or Farstream."), ++#endif ++ structure); ++ error_emitted = TRUE; ++ break; ++ case FS_ERROR_NETWORK: ++ purple_media_error_fs(priv->media, _("Network error."), ++ structure); ++ error_emitted = TRUE; ++ purple_media_end(priv->media, NULL, NULL); ++ break; ++ case FS_ERROR_NEGOTIATION_FAILED: ++ purple_media_error_fs(priv->media, ++ _("Codec negotiation failed. " ++ "This problem might be resolved by installing " ++ "more GStreamer codecs."), ++ structure); ++ error_emitted = TRUE; ++ purple_media_end(priv->media, NULL, NULL); ++ break; + case FS_ERROR_NO_CODECS: +- purple_media_error(priv->media, _("No codecs" +- " found. Install some" +- " GStreamer codecs found" +- " in GStreamer plugins" +- " packages.")); ++ purple_media_error(priv->media, ++ _("No codecs found. " ++ "Install some GStreamer codecs found " ++ "in GStreamer plugins packages.")); ++ error_emitted = TRUE; + purple_media_end(priv->media, NULL, NULL); + break; + #ifdef HAVE_FARSIGHT + case FS_ERROR_NO_CODECS_LEFT: +- purple_media_error(priv->media, _("No codecs" +- " left. Your codec" +- " preferences in" +- " fs-codecs.conf are too" +- " strict.")); ++ purple_media_error(priv->media, ++ _("No codecs left. Your codec preferences " ++ "in fs-codecs.conf are too strict.")); ++ error_emitted = TRUE; + purple_media_end(priv->media, NULL, NULL); + break; ++ case FS_ERROR_CONNECTION_FAILED: ++ purple_media_error(priv->media, ++ _("Could not connect to the remote party")); ++ error_emitted = TRUE; ++ break; + case FS_ERROR_UNKNOWN_CNAME: + /* + * Unknown CName is only a problem for the +@@ -975,22 +1055,22 @@ gst_handle_message_element(GstBus *bus, + "farstream-error: %i: %s\n", + #endif + error_no, +- gst_structure_get_string( +- msg->structure, "error-msg")); ++ gst_structure_get_string(structure, "error-msg")); + break; + } + + if (FS_ERROR_IS_FATAL(error_no)) { ++ if (!error_emitted) + #ifdef HAVE_FARSIGHT +- purple_media_error(priv->media, _("A non-recoverable " +- "Farsight2 error has occurred.")); ++ purple_media_error(priv->media, ++ _("A non-recoverable Farsight2 error has occurred.")); + #else +- purple_media_error(priv->media, _("A non-recoverable " +- "Farstream error has occurred.")); ++ purple_media_error(priv->media, ++ _("A non-recoverable Farstream error has occurred.")); + #endif + purple_media_end(priv->media, NULL, NULL); + } +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-new-local-candidate")) { + #else +@@ -1005,9 +1085,9 @@ gst_handle_message_element(GstBus *bus, + PurpleMediaBackendFs2Stream *media_stream; + gchar *name; + +- value = gst_structure_get_value(msg->structure, "stream"); ++ value = gst_structure_get_value(structure, "stream"); + stream = g_value_get_object(value); +- value = gst_structure_get_value(msg->structure, "candidate"); ++ value = gst_structure_get_value(structure, "candidate"); + local_candidate = g_value_get_boxed(value); + + session = get_session_from_fs_stream(self, stream); +@@ -1029,7 +1109,7 @@ gst_handle_message_element(GstBus *bus, + g_signal_emit_by_name(self, "new-candidate", + session->id, name, candidate); + g_object_unref(candidate); +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-local-candidates-prepared")) { + #else +@@ -1041,7 +1121,7 @@ gst_handle_message_element(GstBus *bus, + PurpleMediaBackendFs2Session *session; + gchar *name; + +- value = gst_structure_get_value(msg->structure, "stream"); ++ value = gst_structure_get_value(structure, "stream"); + stream = g_value_get_object(value); + session = get_session_from_fs_stream(self, stream); + +@@ -1051,7 +1131,7 @@ gst_handle_message_element(GstBus *bus, + + g_signal_emit_by_name(self, "candidates-prepared", + session->id, name); +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-new-active-candidate-pair")) { + #else +@@ -1066,13 +1146,11 @@ gst_handle_message_element(GstBus *bus, + PurpleMediaCandidate *lcandidate, *rcandidate; + gchar *name; + +- value = gst_structure_get_value(msg->structure, "stream"); ++ value = gst_structure_get_value(structure, "stream"); + stream = g_value_get_object(value); +- value = gst_structure_get_value(msg->structure, +- "local-candidate"); ++ value = gst_structure_get_value(structure, "local-candidate"); + local_candidate = g_value_get_boxed(value); +- value = gst_structure_get_value(msg->structure, +- "remote-candidate"); ++ value = gst_structure_get_value(structure, "remote-candidate"); + remote_candidate = g_value_get_boxed(value); + + g_object_get(stream, "participant", &participant, NULL); +@@ -1089,7 +1167,7 @@ gst_handle_message_element(GstBus *bus, + + g_object_unref(lcandidate); + g_object_unref(rcandidate); +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-recv-codecs-changed")) { + #else +@@ -1099,7 +1177,7 @@ gst_handle_message_element(GstBus *bus, + GList *codecs; + FsCodec *codec; + +- value = gst_structure_get_value(msg->structure, "codecs"); ++ value = gst_structure_get_value(structure, "codecs"); + codecs = g_value_get_boxed(value); + codec = codecs->data; + +@@ -1110,7 +1188,7 @@ gst_handle_message_element(GstBus *bus, + "farstream-recv-codecs-changed: %s\n", + #endif + codec->encoding_name); +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-component-state-changed")) { + #else +@@ -1121,9 +1199,9 @@ gst_handle_message_element(GstBus *bus, + guint component; + const gchar *state; + +- value = gst_structure_get_value(msg->structure, "state"); ++ value = gst_structure_get_value(structure, "state"); + fsstate = g_value_get_enum(value); +- value = gst_structure_get_value(msg->structure, "component"); ++ value = gst_structure_get_value(structure, "component"); + component = g_value_get_uint(value); + + switch (fsstate) { +@@ -1158,7 +1236,7 @@ gst_handle_message_element(GstBus *bus, + #endif + "component: %u state: %s\n", + component, state); +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-send-codec-changed")) { + #else +@@ -1168,7 +1246,7 @@ gst_handle_message_element(GstBus *bus, + FsCodec *codec; + gchar *codec_str; + +- value = gst_structure_get_value(msg->structure, "codec"); ++ value = gst_structure_get_value(structure, "codec"); + codec = g_value_get_boxed(value); + codec_str = fs_codec_to_string(codec); + +@@ -1181,7 +1259,7 @@ gst_handle_message_element(GstBus *bus, + codec_str); + + g_free(codec_str); +- } else if (gst_structure_has_name(msg->structure, ++ } else if (gst_structure_has_name(structure, + #ifdef HAVE_FARSIGHT + "farsight-codecs-changed")) { + #else +@@ -1191,7 +1269,7 @@ gst_handle_message_element(GstBus *bus, + FsSession *fssession; + GList *sessions; + +- value = gst_structure_get_value(msg->structure, "session"); ++ value = gst_structure_get_value(structure, "session"); + fssession = g_value_get_object(value); + sessions = g_hash_table_get_values(priv->sessions); + +@@ -1635,7 +1713,11 @@ create_src(PurpleMediaBackendFs2 *self, + srcpad = gst_element_get_static_pad(session->srcvalve, "src"); + g_object_set(volume, "volume", input_volume, NULL); + } else { ++#if GST_CHECK_VERSION(1,0,0) ++ srcpad = gst_element_get_request_pad(session->tee, "src_%u"); ++#else + srcpad = gst_element_get_request_pad(session->tee, "src%d"); ++#endif + } + + purple_debug_info("backend-fs2", "connecting pad: %s\n", +@@ -1645,11 +1727,14 @@ create_src(PurpleMediaBackendFs2 *self, + gst_object_unref(session->src); + gst_object_unref(sinkpad); + +- gst_element_set_state(session->src, GST_STATE_PLAYING); +- + purple_media_manager_create_output_window(purple_media_get_manager( + priv->media), priv->media, sess_id, NULL); + ++ purple_debug_info("backend-fs2", "create_src: setting source " ++ "state to GST_STATE_PLAYING - it may hang here on win32\n"); ++ gst_element_set_state(session->src, GST_STATE_PLAYING); ++ purple_debug_info("backend-fs2", "create_src: state set\n"); ++ + return TRUE; + } + +@@ -1853,14 +1938,10 @@ src_pad_added_cb(FsStream *fsstream, Gst + * audioresample ! audioconvert ! realsink + */ + stream->queue = gst_element_factory_make("queue", NULL); +- stream->volume = gst_element_factory_make( +- "volume", NULL); +- g_object_set(stream->volume, "volume", +- output_volume, NULL); +- stream->level = gst_element_factory_make( +- "level", NULL); +- stream->src = gst_element_factory_make( +- "liveadder", NULL); ++ stream->volume = gst_element_factory_make("volume", NULL); ++ g_object_set(stream->volume, "volume", output_volume, NULL); ++ stream->level = gst_element_factory_make("level", NULL); ++ stream->src = gst_element_factory_make("liveadder", NULL); + sink = purple_media_manager_get_element( + purple_media_get_manager(priv->media), + PURPLE_MEDIA_RECV_AUDIO, priv->media, +@@ -1879,10 +1960,12 @@ src_pad_added_cb(FsStream *fsstream, Gst + gst_element_link(stream->queue, stream->volume); + sink = stream->queue; + } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) { +- stream->src = gst_element_factory_make( +- "fsfunnel", NULL); +- sink = gst_element_factory_make( +- "fakesink", NULL); ++#if GST_CHECK_VERSION(1,0,0) ++ stream->src = gst_element_factory_make("funnel", NULL); ++#else ++ stream->src = gst_element_factory_make("fsfunnel", NULL); ++#endif ++ sink = gst_element_factory_make("fakesink", NULL); + g_object_set(G_OBJECT(sink), "async", FALSE, NULL); + gst_bin_add(GST_BIN(priv->confbin), sink); + gst_element_set_state(sink, GST_STATE_PLAYING); +@@ -1896,7 +1979,11 @@ src_pad_added_cb(FsStream *fsstream, Gst + gst_element_link_many(stream->src, stream->tee, sink, NULL); + } + ++#if GST_CHECK_VERSION(1,0,0) ++ sinkpad = gst_element_get_request_pad(stream->src, "sink_%u"); ++#else + sinkpad = gst_element_get_request_pad(stream->src, "sink%d"); ++#endif + gst_pad_link(srcpad, sinkpad); + gst_object_unref(sinkpad); + +@@ -2078,8 +2165,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS + if (!fs_stream_set_transmitter(fsstream, transmitter, + _params, _num_params, &err)) { + purple_debug_error("backend-fs2", +- "Could not set transmitter %s: %s.\n", +- transmitter, err->message); ++ "Could not set transmitter %s: %s.\n", ++ transmitter, err ? err->message : NULL); + g_clear_error(&err); + g_free(_params); + return FALSE; +@@ -2398,14 +2485,44 @@ purple_media_backend_fs2_set_send_codec( + return TRUE; + } + ++static const gchar ** ++purple_media_backend_fs2_get_available_params(void) ++{ ++ static const gchar *supported_params[] = { ++ "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note", ++ "sdes-phone", "sdes-tool", NULL ++ }; ++ ++ return supported_params; ++} ++ ++static const gchar* ++param_to_sdes_type(const gchar *param) ++{ ++ const gchar **supported = purple_media_backend_fs2_get_available_params(); ++ static const gchar *sdes_types[] = { ++ "cname", "email", "location", "name", "note", "phone", "tool", NULL ++ }; ++ guint i; ++ ++ for (i = 0; supported[i] != NULL; ++i) { ++ if (!strcmp(param, supported[i])) { ++ return sdes_types[i]; ++ } ++ } ++ ++ return NULL; ++} ++ + static void + purple_media_backend_fs2_set_params(PurpleMediaBackend *self, + guint num_params, GParameter *params) + { + PurpleMediaBackendFs2Private *priv; +- const gchar **supported = purple_media_backend_fs2_get_available_params(); +- const gchar **p; + guint i; ++#ifndef HAVE_FARSIGHT ++ GstStructure *sdes; ++#endif + + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); + +@@ -2418,27 +2535,30 @@ purple_media_backend_fs2_set_params(Purp + return; + } + ++#ifdef HAVE_FARSIGHT + for (i = 0; i != num_params; ++i) { +- for (p = supported; *p != NULL; ++p) { +- if (!strcmp(params[i].name, *p)) { +- g_object_set(priv->conference, +- params[i].name, g_value_get_string(¶ms[i].value), +- NULL); +- break; +- } ++ if (param_to_sdes_type(params[i].name)) { ++ g_object_set(priv->conference, ++ params[i].name, g_value_get_string(¶ms[i].value), ++ NULL); + } + } +-} ++#else ++ g_object_get(G_OBJECT(priv->conference), "sdes", &sdes, NULL); + +-static const gchar ** +-purple_media_backend_fs2_get_available_params(void) +-{ +- static const gchar *supported_params[] = { +- "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note", +- "sdes-phone", "sdes-tool", NULL +- }; ++ for (i = 0; i != num_params; ++i) { ++ const gchar *sdes_type = param_to_sdes_type(params[i].name); ++ if (!sdes_type) ++ continue; + +- return supported_params; ++ gst_structure_set(sdes, sdes_type, ++ G_TYPE_STRING, g_value_get_string(¶ms[i].value), ++ NULL); ++ } ++ ++ g_object_set(G_OBJECT(priv->conference), "sdes", sdes, NULL); ++ gst_structure_free(sdes); ++#endif /* HAVE_FARSIGHT */ + } + static gboolean + send_dtmf_callback(gpointer userdata) +Index: pidgin-2.10.11/libpurple/mediamanager.c +=================================================================== +--- pidgin-2.10.11.orig/libpurple/mediamanager.c ++++ pidgin-2.10.11/libpurple/mediamanager.c +@@ -44,7 +44,12 @@ + #else + #include + #endif ++ ++#if GST_CHECK_VERSION(1,0,0) ++#include ++#else + #include ++#endif + + /** @copydoc _PurpleMediaManagerPrivate */ + typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate; +@@ -270,8 +275,11 @@ purple_media_manager_get_pipeline(Purple + gst_bus_add_signal_watch(GST_BUS(bus)); + g_signal_connect(G_OBJECT(bus), "message", + G_CALLBACK(pipeline_bus_call), manager); +- gst_bus_set_sync_handler(bus, +- gst_bus_sync_signal_handler, NULL); ++#if GST_CHECK_VERSION(1,0,0) ++ gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL, NULL); ++#else ++ gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL); ++#endif + gst_object_unref(bus); + + filename = g_build_filename(purple_user_dir(), +@@ -405,20 +413,31 @@ request_pad_unlinked_cb(GstPad *pad, Gst + { + GstElement *parent = GST_ELEMENT_PARENT(pad); + GstIterator *iter; ++#if GST_CHECK_VERSION(1,0,0) ++ GValue tmp = G_VALUE_INIT; ++#endif + GstPad *remaining_pad; + GstIteratorResult result; + +- gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad); ++ gst_element_release_request_pad(parent, pad); + + iter = gst_element_iterate_src_pads(parent); + ++#if GST_CHECK_VERSION(1,0,0) ++ result = gst_iterator_next(iter, &tmp); ++#else + result = gst_iterator_next(iter, (gpointer)&remaining_pad); ++#endif + + if (result == GST_ITERATOR_DONE) { + gst_element_set_locked_state(parent, TRUE); + gst_element_set_state(parent, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent); + } else if (result == GST_ITERATOR_OK) { ++#if GST_CHECK_VERSION(1,0,0) ++ remaining_pad = g_value_get_object(&tmp); ++ g_value_reset(&tmp); ++#endif + gst_object_unref(remaining_pad); + } + +@@ -456,7 +475,11 @@ purple_media_manager_get_video_caps(Purp + { + #ifdef USE_VV + if (manager->priv->video_caps == NULL) ++#if GST_CHECK_VERSION(1,0,0) ++ manager->priv->video_caps = gst_caps_from_string("video/x-raw," ++#else + manager->priv->video_caps = gst_caps_from_string("video/x-raw-yuv," ++#endif + "width=[250,352], height=[200,288], framerate=[1/1,20/1]"); + return manager->priv->video_caps; + #else +@@ -539,7 +562,11 @@ purple_media_manager_get_element(PurpleM + g_free(id); + + tee = gst_bin_get_by_name(GST_BIN(ret), "tee"); ++#if GST_CHECK_VERSION(1,0,0) ++ pad = gst_element_get_request_pad(tee, "src_%u"); ++#else + pad = gst_element_get_request_pad(tee, "src%d"); ++#endif + gst_object_unref(tee); + ghost = gst_ghost_pad_new(NULL, pad); + gst_object_unref(pad); +@@ -730,9 +757,12 @@ window_id_cb(GstBus *bus, GstMessage *ms + { + GstElement *sink; + +- if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT || +- !gst_structure_has_name(msg->structure, +- "prepare-xwindow-id")) ++ if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ++#if GST_CHECK_VERSION(1,0,0) ++ || !gst_is_video_overlay_prepare_window_handle_message(msg)) ++#else ++ || !gst_structure_has_name(msg->structure, "prepare-xwindow-id")) ++#endif + return; + + sink = GST_ELEMENT(GST_MESSAGE_SRC(msg)); +@@ -746,8 +776,16 @@ window_id_cb(GstBus *bus, GstMessage *ms + | G_SIGNAL_MATCH_DATA, 0, 0, NULL, + window_id_cb, ow); + +- gst_x_overlay_set_xwindow_id(GST_X_OVERLAY( +- GST_MESSAGE_SRC(msg)), ow->window_id); ++#if GST_CHECK_VERSION(1,0,0) ++ gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)), ++ ow->window_id); ++#elif GST_CHECK_VERSION(0,10,31) ++ gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), ++ ow->window_id); ++#else ++ gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), ++ ow->window_id); ++#endif + } + #endif + +@@ -772,17 +810,19 @@ purple_media_manager_create_output_windo + (participant == ow->participant)) && + !strcmp(session_id, ow->session_id)) { + GstBus *bus; +- GstElement *queue, *colorspace; ++ GstElement *queue, *convert; + GstElement *tee = purple_media_get_tee(media, + session_id, participant); + + if (tee == NULL) + continue; + +- queue = gst_element_factory_make( +- "queue", NULL); +- colorspace = gst_element_factory_make( +- "ffmpegcolorspace", NULL); ++ queue = gst_element_factory_make("queue", NULL); ++#if GST_CHECK_VERSION(1,0,0) ++ convert = gst_element_factory_make("videoconvert", NULL); ++#else ++ convert = gst_element_factory_make("ffmpegcolorspace", NULL); ++#endif + ow->sink = purple_media_manager_get_element( + manager, PURPLE_MEDIA_RECV_VIDEO, + ow->media, ow->session_id, +@@ -795,7 +835,7 @@ purple_media_manager_create_output_windo + if (g_object_class_find_property(klass, + "sync")) + g_object_set(G_OBJECT(ow->sink), +- "sync", "FALSE", NULL); ++ "sync", FALSE, NULL); + if (g_object_class_find_property(klass, + "async")) + g_object_set(G_OBJECT(ow->sink), +@@ -803,7 +843,7 @@ purple_media_manager_create_output_windo + } + + gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)), +- queue, colorspace, ow->sink, NULL); ++ queue, convert, ow->sink, NULL); + + bus = gst_pipeline_get_bus(GST_PIPELINE( + manager->priv->pipeline)); +@@ -812,10 +852,10 @@ purple_media_manager_create_output_windo + gst_object_unref(bus); + + gst_element_set_state(ow->sink, GST_STATE_PLAYING); +- gst_element_set_state(colorspace, GST_STATE_PLAYING); ++ gst_element_set_state(convert, GST_STATE_PLAYING); + gst_element_set_state(queue, GST_STATE_PLAYING); +- gst_element_link(colorspace, ow->sink); +- gst_element_link(queue, colorspace); ++ gst_element_link(convert, ow->sink); ++ gst_element_link(queue, convert); + gst_element_link(tee, queue); + } + } +Index: pidgin-2.10.11/pidgin/Makefile.am +=================================================================== +--- pidgin-2.10.11.orig/pidgin/Makefile.am ++++ pidgin-2.10.11/pidgin/Makefile.am +@@ -151,6 +151,7 @@ pidgin_LDADD = \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) \ + $(GSTREAMER_LIBS) \ ++ $(GSTVIDEO_LIBS) \ + $(XSS_LIBS) \ + $(SM_LIBS) \ + $(INTLLIBS) \ +Index: pidgin-2.10.11/pidgin/gtkmedia.c +=================================================================== +--- pidgin-2.10.11.orig/pidgin/gtkmedia.c ++++ pidgin-2.10.11/pidgin/gtkmedia.c +@@ -43,7 +43,9 @@ + #endif + #include + ++#if !GST_CHECK_VERSION(1,0,0) + #include ++#endif + + #define PIDGIN_TYPE_MEDIA (pidgin_media_get_type()) + #define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia)) +@@ -590,6 +592,9 @@ pidgin_media_error_cb(PidginMedia *media + if (conv != NULL) + purple_conversation_write(conv, NULL, error, + PURPLE_MESSAGE_ERROR, time(NULL)); ++ else ++ purple_notify_error(NULL, NULL, _("Media error"), error); ++ + gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), + 0, error); + } +Index: pidgin-2.10.11/pidgin/plugins/vvconfig.c +=================================================================== +--- pidgin-2.10.11.orig/pidgin/plugins/vvconfig.c ++++ pidgin-2.10.11/pidgin/plugins/vvconfig.c +@@ -26,7 +26,11 @@ + #include "gtkutils.h" + #include "gtkprefs.h" + ++#if GST_CHECK_VERSION(1,0,0) ++#include ++#else + #include ++#endif + + /* container window for showing a stand-alone configurator */ + static GtkWidget *window = NULL; +@@ -81,8 +85,10 @@ get_element_devices(const gchar *element + GList *ret = NULL; + GstElement *element; + GObjectClass *klass; ++#if !GST_CHECK_VERSION(1,0,0) + GstPropertyProbe *probe; + const GParamSpec *pspec; ++#endif + + ret = g_list_prepend(ret, (gpointer)_("Default")); + ret = g_list_prepend(ret, ""); +@@ -103,6 +109,10 @@ get_element_devices(const gchar *element + return g_list_reverse(ret); + } + ++#if GST_CHECK_VERSION(1,0,0) ++ purple_debug_info("vvconfig", "'%s' - gstreamer-1.0 doesn't suport " ++ "property probing\n", element_name); ++#else + if (!g_object_class_find_property(klass, "device") || + !GST_IS_PROPERTY_PROBE(element) || + !(probe = GST_PROPERTY_PROBE(element)) || +@@ -155,6 +165,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS + gst_element_set_state(element, GST_STATE_NULL); + } + } ++#endif + gst_object_unref(element); + + return g_list_reverse(ret); +@@ -168,8 +179,13 @@ get_element_plugins(const gchar **plugin + ret = g_list_prepend(ret, "Default"); + ret = g_list_prepend(ret, ""); + for (; plugins[0] && plugins[1]; plugins += 2) { ++#if GST_CHECK_VERSION(1,0,0) ++ if (gst_registry_check_feature_version(gst_registry_get(), ++ plugins[0], 0, 0, 0)) { ++#else + if (gst_default_registry_check_feature_version( + plugins[0], 0, 0, 0)) { ++#endif + ret = g_list_prepend(ret, (gpointer)plugins[1]); + ret = g_list_prepend(ret, (gpointer)plugins[0]); + } +@@ -588,7 +604,13 @@ gst_msg_db_to_percent(GstMessage *msg, g + + list = gst_structure_get_value( + gst_message_get_structure(msg), value_name); ++#if GST_CHECK_VERSION(1,0,0) ++G_GNUC_BEGIN_IGNORE_DEPRECATIONS ++ value = g_value_array_get_nth(g_value_get_boxed(list), 0); ++G_GNUC_END_IGNORE_DEPRECATIONS ++#else + value = gst_value_list_get_value(list, 0); ++#endif + value_db = g_value_get_double(value); + percent = pow(10, value_db / 20); + return (percent > 1.0) ? 1.0 : percent; +@@ -604,7 +626,7 @@ static gboolean + gst_bus_cb(GstBus *bus, GstMessage *msg, BusCbCtx *ctx) + { + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && +- gst_structure_has_name(msg->structure, "level")) { ++ gst_structure_has_name(gst_message_get_structure(msg), "level")) { + + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + gchar *name = gst_element_get_name(src); diff --git a/pidgin.changes b/pidgin.changes index c8c4dc2..4c47a7e 100644 --- a/pidgin.changes +++ b/pidgin.changes @@ -1,3 +1,28 @@ +------------------------------------------------------------------- +Tue Aug 11 17:16:56 UTC 2015 - zaitor@opensuse.org + +- Add a Recommends: gstreamer-plugins-good, this plugin provides + wav support that pidgin needs. + +------------------------------------------------------------------- +Tue Jun 30 14:50:50 UTC 2015 - dimstar@opensuse.org + +- Re-attempt port to GStreamer 1.0 (based on work by David): + + Add patches, from upstream: + - pidgin-port-to-gst-1.0.patch + - pidgin-2.10.11-gst-references.patch + - pidgin-2.10.11-add-dtmf-support.patch + - pidgin-2.10.11-application-media.patch + - pidgin-2.10.11-init-media-optional.patch + - pidgin-2.10.11-send-video-enum.patch + - pidgin-2.10.11-private-media.patch + + Replace gstreamer-0_10-devel and + gstreamer-0_10-plugins-base-devel BuildRequires with + pkgconfig(gstreamer-1.0), pkgconfig(gstreamer-video-1.0) and + pkgconfig(farstream-0.2). + + Switch --disable-vv configure paramter to --enable-vv. + + Pass --with-gstreamer=1.0 to configure. + ------------------------------------------------------------------- Tue Jan 20 10:04:54 UTC 2015 - tbehrens@suse.com diff --git a/pidgin.spec b/pidgin.spec index 44a6052..4d50e55 100644 --- a/pidgin.spec +++ b/pidgin.spec @@ -1,7 +1,7 @@ # # spec file for package pidgin # -# Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -23,7 +23,6 @@ Release: 0 Summary: Multiprotocol Instant Messaging Client License: GPL-2.0+ Group: Productivity/Networking/Instant Messenger -# FIXME: Remove unconditional --disable-vv parameter from configure once pidgin is ported to farstream 0.2 Url: http://pidgin.im/ Source: http://downloads.sourceforge.net/project/pidgin/Pidgin/2.10.11/%{name}-%{version}.tar.gz Source1: %{name}-prefs.xml @@ -35,6 +34,19 @@ Patch3: %{name}-gnome-keyring.patch Patch4: %{name}-fix-perl-build.patch # PATCH-FIX-UPSTREAM pidgin-crash-missing-gst-registry.patch bnc#866455 pidgin.im#16224 cxiong@suse.com -- Fix crash when GST registry cache file is missing. Patch6: pidgin-crash-missing-gst-registry.patch +Patch7: pidgin-2.10.11-send-video-enum.patch +# PATCH-FEATURE-UPSTREAM pidgin-2.10.11-gst-references.patch dimstar@opensuse.org -- http://hg.pidgin.im/pidgin/main/rev/b52be4fef1de +Patch8: pidgin-2.10.11-gst-references.patch +# PATCH-FEATURE-UPSTREAM pidgin-2.10.11-add-dtmf-support.patch dimstar@opensuse.org -- http://hg.pidgin.im/pidgin/main/rev/6b4576edf2a6 +Patch9: pidgin-2.10.11-add-dtmf-support.patch +# PATCH-FEATURE-UPSTREAM pidgin-port-to-gst-1.0.patch dimstar@opensuse.org -- Port to GStreamer 1.0 / farstream 0.2; taken from https://pidgin.im/pipermail/devel/2015-March/023645.html +Patch10: pidgin-port-to-gst-1.0.patch +# PATCH-FIX-UPSTREAM pidgin-2.10.11-init-media-optional.patch dimstar@opensuse.org -- make "init-media" signal handler optional +Patch11: pidgin-2.10.11-init-media-optional.patch +# PATCH-FIX-UPSTREAM pidgin-2.10.11-private-media.patch dimstar@opensuse.org -- Add Private media API. +Patch12: pidgin-2.10.11-private-media.patch +# PATCH-FIX-UPSTREAM pidgin-2.10.11-application-media.patch dimstar@opensuse.org -- Add application media type and APIs, pidgin.im#16315 +Patch13: pidgin-2.10.11-application-media.patch BuildRequires: NetworkManager-devel # Can use external libzephyr. BuildRequires: cyrus-sasl-devel @@ -44,8 +56,6 @@ BuildRequires: doxygen BuildRequires: fdupes BuildRequires: gconf2-devel BuildRequires: graphviz -BuildRequires: gstreamer-0_10-devel -BuildRequires: gstreamer-0_10-plugins-base-devel BuildRequires: gtk-doc BuildRequires: gtkspell-devel BuildRequires: intltool @@ -66,6 +76,9 @@ BuildRequires: silc-toolkit-devel BuildRequires: startup-notification-devel BuildRequires: tk-devel BuildRequires: update-desktop-files +BuildRequires: pkgconfig(farstream-0.2) +BuildRequires: pkgconfig(gstreamer-1.0) +BuildRequires: pkgconfig(gstreamer-video-1.0) Requires: perl-base = %{perl_version} %if 0%{?suse_version} <= 1110 Recommends: %{name}-emoticons-nld @@ -108,6 +121,7 @@ Requires: ca-certificates-mozilla %else Requires: openssl-certs %endif +Recommends: gstreamer-plugins-good %description Pidgin is a chat program which lets you log in to accounts on multiple @@ -255,6 +269,13 @@ translation-update-upstream %if 0%{?suse_version} >= 1310 %patch6 -p1 %endif +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 # Change Myanmar/Myanmar to Myanmar. mv -f po/my_MM.po po/my.po @@ -273,13 +294,14 @@ autoreconf -fi --enable-cyrus-sasl \ --enable-dbus \ --enable-gstreamer \ + --with-gstreamer=1.0 \ %if 0%{?suse_version} <= 1110 --enable-gnome-keyring \ %endif --enable-nm \ --enable-dbus \ --enable-devhelp \ - --disable-vv \ + --enable-vv \ --with-tclconfig=%{_libdir} \ --with-tkconfig=%{_libdir} \ --with-system-ssl-certs=%{_sysconfdir}/ssl/certs/